# Changelog

All notable changes to `mgf-fed` are documented here. Format: [Keep a Changelog](https://keepachangelog.com/);
versioning: [SemVer](https://semver.org/).

## [0.18.0] - 2026-07-31

### Added
- **`mgf-fed audit` — the cross-layer gate suite.** Eight checks, each comparing one
  layer of the federation to another: `declared → committed → published → installed
  → live`.

  | check | boundary |
  |---|---|
  | `fleet` | declared ↔ committed |
  | `feedback-status` | declared ↔ declared |
  | `release-currency` | committed ↔ published |
  | `installed-currency` | installed ↔ published |
  | `governance` | live ↔ declared |
  | `ci-freshness` | live ↔ committed |
  | `secret-scopes` | declared ↔ committed |
  | `pypi-namespace` | published ↔ declared |

  These are the **five orphan verifiers from the court workspace, plus two more,
  folded into the gate path**. On 2026-07-30 there were five hand-written verifiers
  in `mgf/tools/` referenced by **zero pipelines** — each written after a real
  incident, run once during the review that produced it, never again. The knowledge
  was captured; the enforcement was not, so the same classes recurred and were
  re-diagnosed from scratch. `verify_release_currency.py` documented the "published
  0.4.10 and main's 0.4.10 are different code" class perfectly, and that class
  recurred the day it was re-read, because nothing ran it.

  Two properties make this more than a pile of checks:

  1. **Every check names the boundary it crossed.** A finding reads *committed ↔
     published*, not just "problem" — including a check that was skipped, which still
     reports what it *would* have compared.
  2. **A check that could not run is a FAILURE, not a pass.** Without `--offline`, a
     missing token or unreachable index exits non-zero. "I could not verify" must
     never share an exit code with "I verified" — that equivalence is what let a dead
     webhook look like a healthy repo for three weeks.

- **`--write` / `--check-artifact`** — the audit needs the whole federation on disk
  plus credentials, so it cannot run in a single-repo CI clone. The expensive half
  runs where the fleet is and writes `mgf-standard/AUDIT.json`; the cheap half gates
  that artifact on four questions it can answer about itself — stamped, fresh,
  **complete**, clean — and needs no clones, network or credentials. An artifact from
  an `--offline` run records what did not run and therefore **cannot** satisfy the
  gate: zero findings across the checks that ran is not a clean audit.

  Live first run against the real fleet: **8 findings, 0 false positives** — and it
  independently found the unreleased `mgf-secrets` fix that had to be spotted by hand
  hours earlier.

### Fixed
- **`RELEASE_IGNORED` had gone stale**, found by this gate's own first live run: it
  reported 32 of 33 distributions stale, and 25 said only *"shipped files changed:
  AGENTS.md"*. The fleet-wide `AGENTS.md` ripple postdated the tuned exclusion list,
  so a faithful port inherited a list that had decayed — the same over-reporting the
  list was written to prevent, arriving by a different route. **A gate's exclusion
  list is not write-once**, and its decay shows up as noise long before anyone calls
  it a bug. 32 → 6 findings, all real.

### Added
- **`ci-unknown-key` drift finding** — an unrecognised `[tool.mgf-fed]` carve-out key
  is now a *finding*, not a stderr note. `drift-check` reports it, `scaffold` prints
  it as manual work and returns **1**.

  This closes the one CI-config defect the byte-render check structurally cannot
  catch: a key the renderer does not read shapes nothing, so the render is identical
  to itself and `ci-generated` stays silent while the author believes a knob is on.
  `mgf-uitest-desktop` declared `qt = true` (a knob that has never existed, written
  by analogy with `browser = true`); it parsed, printed a note into the middle of a
  46-repo sweep, let `scaffold` report success, and rendered a Qt pipeline with no Qt
  libraries — three red runs before anyone read the note.

  The message now names the consequence ("the rendered pipeline is NOT shaped by
  them"), lists the known keys, and suggests the right one from three sources: a
  **tombstone** table (deleted knobs get the reason — `skip_editable`), a **semantic
  alias** table (`qt` → `apt_packages`; not a typo of anything, so no edit distance
  finds it), then a lexical near-match for genuine typos.

  The render itself still never fails on an unknown key — a crashed renderer would
  strand the fleet. Verified against the real pre-fix `pyproject.toml` recovered from
  `mgf-uitest-desktop` history: the check fires and points at `apt_packages`.

## [0.17.0] - 2026-07-30

The **tooling round** of 2026-07-29/30. MINOR: one new verb and two behaviour fixes
to `catch-up --apply`; no breaking change to any existing verb.

Found by running `catch-up --apply` across **41 repos in one night** — none of these
defects is visible in a single-repo run.

### Added
- **`mgf-fed conformance`** — the authoritative fleet conformance audit is a command,
  not a file path. `mgf-fed status` dropped its `conf%` column under CIR-15 and its
  footer pointed at `mgf-standard/scripts/fleet_conformance_audit.py`; a path cannot
  be listed, installed or discovered, so the fleet's authoritative measure was the
  least reachable thing in it. Its committed tracker had been asserting a clean fleet
  since 2026-07-04. Streams (the audit runs for minutes), bounded at 3600s, warns when
  the tracker would be written UNSTAMPED, and exposes the auditor's repeatable search
  root as `--extra-root` so it cannot collide with mgf-fed's `--root`.

### Fixed
- **`catch-up --apply` no longer overrides a declared CFM-04 decline (PAPER-126).**
  In `mgf-standard` it injected `mgf-test-supervisor` into dev deps — the dependency
  that repo permanently declines because it closes a `mgf-standard →
  mgf-test-supervisor → mgf-common → mgf-standard` **cycle**, with the rationale
  written in the three comment lines directly above the insertion point. `--apply` is
  advertised as *mechanical*; a mechanical tool that silently overrides a reasoned
  decline is not mechanical, it is opinionated in a direction nobody reviewed.
  `_rule_is_declined()` now reads the repo's own `MGF_STANDARDS_CONFORMANCE.md`.
- **`catch-up --apply` no longer corrupts a trailing comment (PAPER-126).** The
  dev-deps insert appended the separating comma to the last *line*, so a list ending
  in an explanation became `# …non-adoption-is-valid.md).,`. TOML parses that — the
  comma is inside a comment — so nothing caught it.
- **`catch-up --apply` converges on the CLAUDE.md auto-block (PAPER-127).** It claimed
  to migrate the legacy `mgf-common catch-up` marker and never did: affected files
  carry **two** auto-blocks, the writer took the first match and the checker tested for
  the legacy marker *anywhere*, so the warn could never clear. **21 of 44 repos** were
  stuck. `_find_begin_marker` now returns the earliest marker and orphan blocks are
  removed before the survivor is refreshed.
- **`fed-tools`: `mgf-secrets` registered; `doctor` sees CLIs and currency; scope
  stated.** The credential plane the workspace rule names as the first thing to ask was
  absent from the front door and uninstalled. `doctor` swept only web consoles, so a
  CLI could never surface as drift; `update --check` was a gate nothing ran and was
  blind to CLIs (it reported 3 stale when 5 were). Every success line now states its own
  scope — `doctor --offline` says outright that currency was **not** checked.

## [0.16.0] - 2026-07-18

The **enforcement half of DP-5** from the 2026-07-17 **CI Support Review**
(`court/reviews/2026-07-17-ci-support-review/`, decision package DP-5 → finding
**CIR-04**, corpus rule **WF-14**). The governance half landed in mgf-standard
0.1.66 (WF-14 went live 2026-07-18); this release makes `drift-check` enforce it.
MINOR: a new gate-floor check is a feature; no breaking change to any existing verb.

### Added
- **`ci-uv-frozen` gate-floor check — enforces WF-14 (CIR-04 / PAPER-125).** A CI
  `uv` verb that re-resolves — `uv run` / `uv export` / `uv sync` / `uv pip install`
  — without `--frozen` re-resolves the dependency graph against the index and
  **rewrites `uv.lock` mid-pipeline** (PAPER-125: an un-frozen CI `uv` verb silently
  swapped 9 pins to pypi relics). `drift-check` now flags every such verb on every
  member's `.woodpecker.yml`, any language, exempt or not — and an audit-shaped
  `uv export` additionally must carry `--no-emit-project` (the project under test is
  not one of its own scanned dependencies). Same architecture as the `ci-clone` /
  `ci-command` gate-floor checks: pure functions `uv_frozen_gap()` + the shared
  `UV_FROZEN_FIX` remediation in `mgf.fed._pipeline`, consumed by `_drift.gate_floor`;
  the new `ci-uv-frozen` kind joins `_GATE_FLOOR_KINDS`. Scanned over
  `command_lines()` (a commented-out `uv run` is not a gate), split per piped/chained
  sub-command so a two-verb line credits `--frozen` to its own invocation. `uv lock`
  (resolve-by-design), `uv tool` (a global install) and `uv --version` are never
  flagged. **A pip-only pipeline runs no such verb → no finding** — the current fleet
  is pip-based, so `drift-check --shape all` stays 40/40 clean (verified).

### Changed
- **`uv.lock` refreshed and committed on the core ring (CIR-04, Part B).** mgf-fed
  carried a stale `uv.lock` (self-version behind, corpus pins behind); it is
  regenerated here so `--frozen` is honoured against a current lock. mgf-fedres and
  mgf-fed-web — which committed no lock at all — gain one via separate lock-only PRs,
  so the WF-14 precondition (a committed lock, WF-11 / §7.1) holds fleet-wide.

## [0.15.0] - 2026-07-17

The **"honest instruments"** round of the 2026-07-17 **CI Support Review**
(`court/reviews/2026-07-17-ci-support-review/CI_SUPPORT_REVIEW.md`, Phase 2 items
2.2 / 2.4 / 2.5 / 2.1 — findings **CIR-16 / CIR-11 / CIR-10 / CIR-15**). MINOR: a
new `ci-stats` verb and new `feedback` check modes; no breaking change to any
existing verb.

### Added
- **`mgf-fed ci-stats` — the first standing CI-health metric (CIR-11).** Before this,
  fleet CI health was an anecdote: the review had to measure its 21.7% red rate with
  throwaway scripts because no standing metric existed. The verb productizes that
  measurement, **reproducibly**, polling **Woodpecker only** (never Codeberg — PAPER-110):
  the last `--limit` pipelines/repo (default 40, the review's method) across the roster,
  reporting **red rate by failed-step class** (a `failure` by its first failed step,
  `clone` when clone-only; an `error` split on the CIR-10 forge config-fetch signature;
  `killed` as its own class), the **restart tax** (commit+event pairs that ran >1 pipeline,
  + total extra runs), **duration p50/p90** (nearest-rank, deterministic), and **per-repo
  outliers** (red rate above the fleet's, ≥2 reds). `--json` for a weekly cron. New module
  `mgf.fed.cli._ci_stats`; every call rides the PAPER-119 429 budget.
- **`mgf-fed feedback --in-repo [PATH]` — the consumer-app rot-gate (CIR-16 (a)).** The
  fleet scan only ever saw registry members, so a **consumer app's** `pending-feedback/`
  outbox was invisible (TelePlasma's 6-paper outbox produced "No un-relayed feedback ✓").
  `--in-repo` scans THIS repo's outbox regardless of roster, so an app can gate its own CI.
  Check-only (relay stays a supervisor action off the federation root).
- **`mgf-fed feedback --fresh` — freshness against a stale clone (CIR-16 (b)).** The gate
  read the dirty **working tree**, so a stale clone missing a just-pushed paper false-greened
  (07-14: 5 papers seen locally vs 7 on origin/main). `--fresh` reads the outbox from
  `origin/main` via a read-only `git show`/`ls-tree` instead. Composes with `--in-repo`.

### Changed
- **`mgf-fed ci-heal` now also restarts transient forge config-fetch ERRORS (CIR-10).**
  Beyond clone-only **failures** (PAPER-109), it restarts **error**-status pipelines whose
  message carries the transient `could not load config from forge` signature — pipelines that
  run **zero steps**, so nothing gates and nothing pages, and which the clone-only signature
  could never see (mgf-alembic #95, mgf-apiprobe #116, sooth-brand #36/#37…). **Same
  once-bound discipline** (skip if any later pipeline exists for the commit; at most one
  restart). Still Woodpecker-only. The error-message extractor + signature are shared with
  `ci-stats` so both read reds identically.
- **`mgf-fed status` drops the `conf%` column (CIR-15).** It regexed emoji from hand-written
  ledgers, ran no checks, and — scoring every repo against rules it never *declared* — ranked
  the fleet **backwards** (`mgf-fed-web`, 0 real gaps at its declared L1, read "32% worst";
  `mgf-fedres`, the one hard fail, read "61%"). A dropped column beats an inverted one: the
  table now prints a pointer to the authoritative scorer,
  `mgf-standard/scripts/fleet_conformance_audit.py` (which honors declared level + N/A +
  documented non-adoption). The honest raw ledger tallies (`conf_ok`/`conf_wip`/`conf_na`)
  remain in `MGF_FED_STATUS.json`; the derived (invertible) `conf_pct` field is removed.

## [0.14.3] - 2026-07-17

Three reviewed fixes from the 2026-07-17 **CI Support Review**
(`court/reviews/2026-07-17-ci-support-review/CI_SUPPORT_REVIEW.md`,
findings CIR-05 / CIR-01 / CIR-03).

### Added
- **`ci-clone` — a new GATE FLOOR check** (`mgf.fed._pipeline.clone_hardening_gap`),
  emitted from `drift-check`'s `gate_floor()` for **every** registry member (any
  language, exempt or not). Every committed `.woodpecker.yml` must carry the hardened
  clone: `partial: false` (the WF-09 full clone that dodges the Codeberg promisor 504)
  **and** `use-ssh: true` (the PAPER-109 SSH clone that retired the flaky short-lived
  OAuth credential). A pipeline with **no** clone block silently inherits Woodpecker's
  default — a partial clone over an OAuth credential — and "an absent block is the most
  invisible kind of drift" (PAPER-128). Evaluated over **non-comment lines only** (a
  commented-out setting is not a setting, and the canonical block's own prose mentions
  `partial: false`); substring philosophy like `missing_commands`, so a bespoke pipeline
  stays free in shape. Excusable via a new `[exempt].clone` policy list (empty default,
  read defensively). Shared remediation string `CLONE_HARDENING_FIX`. Closes CIR-03 /
  PAPER-128's durable ask. Real fleet verified clean: `drift-check --shape all` → 40/40,
  0 items.

### Fixed
- **`_ci_install_safety_drift` read `PIP_INDEX_URL` out of RAW text — a mere COMMENT
  disabled the bare-install check.** A comment merely *mentioning* `PIP_INDEX_URL`
  silently marked the pipeline as index-steered, defeating the dependency-confusion /
  stale-mgf-* bare-`pip install` scan entirely — the exact comment-vs-code class 0.14.2
  fixed for the bare-install scan itself (CIR-05). Env-steering is now judged over
  **non-comment lines**, so a genuine `environment: PIP_INDEX_URL: …` **key line** still
  counts as steered while a comment does not. Regression-tested both directions.
- **The "structurally impossible" dependency-confusion comment in `_woodpecker.py` was
  wrong.** It claimed the devpi index made dependency-confusion structurally impossible;
  true only for names devpi **has a release for**. A name with **no** devpi release (a
  typo'd or pre-declared `mgf-*` dep) falls through `bases: [root/pypi]` to public PyPI
  (proven live 2026-07-17: `+simple/mgf-doc-standard/` → 200; mgf-standard PAPER-123).
  The comment now states the real semantics — published names are mirror-blocked,
  unpublished names fall through until `root/pypi` is severed (CIR-01, decision pending).

## [0.14.0] - 2026-07-16

### Added
- **The WUI-07 `ui-gate` — a console's browser-UI specs, actually run.** A member
  that depends on `mgf-uitest-web` now has a dedicated `ui-gate` step spliced into
  its pipeline at mgf-standard's new `# <<INSERT:ui-gate>>` marker (fragment ships
  in **mgf-standard >= 0.1.64**): install the pinned Chromium, then
  `pytest tests/e2e -m e2e`. Gated on the **dependency**, not a declaration — the
  specs cannot exist without the harness (their conftest imports `mgf.uitest_web`),
  so the tier cannot be added-and-forgotten. Read from the repo's own pyproject
  rather than the registry's `deps`, because a registry regenerated on its own
  cadence would render the gate away while looking fine.

  **Why a dedicated step rather than the specs inside the test jobs — this is the
  load-bearing part.** A console's pytest `addopts` deselect the tier-3 markers by
  default (`-m "not perf and not slow and not stress and not e2e …"`, per TS-18), so
  UI specs left to a normal `pytest -q` run are **silently deselected**: a gate that
  looks present and never runs. The step selects the tier explicitly (a CLI `-m`
  overrides the addopts one), and pytest's exit-5 on an empty selection means a gate
  that ran nothing reds rather than greens.
- **`ci-ui-gate` — a new GATE FLOOR check** (`mgf.fed._pipeline.ui_gate_gap`), wired
  into `drift-check` and the doctors beside `ci-coverage`. Fires when a repo depends
  on the harness but its pipeline never selects the tier — the browser specs exist,
  the harness installs, and **nothing runs them**. Detected on the
  `pytest … -m e2e` invocation, not on a step *named* `ui-gate`: a step name is not
  a gate (PAPER-121), and a commented-out gate is not a gate either.

### Fixed
- **`app-doctor` silently truncated its own checklist under `filterwarnings = ["error"]`.**
  A warning emitted while a check probed an optional surface (e.g. mgf-common's
  observability registries) was raised as an exception under the fleet's own strict
  warning default (TS-05), costing **every check after it** — the operator saw a short
  report and **no error**. It presented as an order-dependent test: once any earlier
  caller had imported the probed module, `sys.modules` served it warning-free and the
  missing checks reappeared, so the doctor's output depended on what had been imported
  before it. Found because a supervisor tier selection happened to probe it first, and
  it reds `mtest --tier 0-2` while a plain `pytest` run stays green (TS-03). The check
  loop now runs under a **scoped** warning suppression: the doctor REPORTS environment
  state, it does not enforce the host's warning policy — and it never changes that
  policy for the caller's own code.
- **The `browser` carve-out shipped in 0.13.0 with no tests.** Covered now, along
  with the new gate: the install renders only under `[tool.mgf-fed] browser = true`,
  lands in all three test jobs, `browser` is a known CI key, the ui-gate does **not**
  imply `browser` (a console's test jobs stay browser-free — only its gate pays the
  download), and a distribution whose name merely *starts with* `mgf-uitest-web`
  does not switch the gate on.

## [0.13.0] - 2026-07-16

### Added
- **`[tool.mgf-fed] browser = true` — an opt-in browser carve-out for the CI
  renderer.** A member that declares it has `playwright install --with-deps
  chromium` spliced into each test job, at the new
  `# <<INSERT:playwright-install>>` markers in mgf-standard's `base.yml`
  (requires `mgf-standard` ≥0.1.63, which ships the companion
  `frag-playwright-install.yml`). `python:*-slim` carries neither the browser
  binary nor its system libraries, so a suite whose product IS driving a real
  browser cannot run in CI at all — let alone reach the TS-06 ≥80% coverage
  floor. Gated exactly like `lint-imports` is gated on an import-linter
  contract existing: `browser` defaults to False, so the marker is dropped for
  the other 40 members and nobody pays the download. `browser` joins
  `_KNOWN_CI_KEYS`, so the key is recognised rather than rejected as unknown
  config. First consumer: `mgf-uitest-web`, whose suite drives each web console
  in a real pinned Chromium.

## [0.12.0] - 2026-07-14

### Added
- **The federation GATE FLOOR** — new `mgf.fed._pipeline`, wired into `drift-check` and
  both `app-doctor` surfaces. Three checks that hold for EVERY registry member, in any
  language, **exempt or not** (closes FEEDBACK PAPER-121 + PAPER-122):
  - `ci-command` — the pipeline must RUN the required gate **commands**, not merely carry
    steps with the right **names**. Sourced from the policy's optional `required_commands`
    key, defaulting to `["ruff check", "ruff format --check", "check_commit_trailers.py"]`
    when it is absent. Applied to `[exempt].woodpecker` repos too: exempt means "we don't
    regenerate your file", not "you may silently drop a federation gate" — the hole that
    let the `ruff format --check` gate miss mgf-hrb / mgf-test-supervisor / **mgf-standard**
    (the repo that authored it) with drift-check staying green.
  - `ci-missing` — a registry member with **no `.woodpecker.yml` at all** (every Woodpecker
    run errors with "could not load config from forge"; no gate has ever run). Checked for
    every member regardless of `--shape`, since a Zig member with no pipeline runs no gates
    either. Excused only by an explicit `[exempt].no_pipeline` entry.
  - `ci-coverage` — a pipeline running `coverage run … -m mgf.test_supervisor` whose
    pyproject lacks `[tool.coverage.run] patch = ["subprocess"]`: the coupling that reds CI
    with **every test passing** ("No data was collected" → "No data to combine" → exit 1).
    Also a FAIL in `app-doctor` (TS-23) and `app-doctor --standards` (the CI-run surface),
    so the fix is named where the confusing failure appears.
  - `ci-ungoverned` — a **cloned `mgf-*` repo the registry does not list** that ships no
    pipeline. Every check we own iterates registry members, so a repo outside the registry
    was outside every gate: mgf-brand (no publishable package, hence never registered) is
    activated on the forge, errors every run, and nothing noticed. Narrow by design — a
    non-registry repo that HAS a pipeline is running gates and is not flagged — which holds
    it to exactly one finding fleet-wide today.
  `drift-check` prints the floor as its own **GATE FLOOR** section — a gate that is not
  running is not the same kind of finding as a file that drifted from the template.
- fed-tools: self-install + liveness + registry-drift checks.

### Fixed
- **`catch-up` no longer demands the deleted `slow` marker.** It kept its own hand-copied
  taxonomy; `mgf-common` 0.50.0 removed `slow` per TS-18 and the checker never followed, so it
  flagged 24 conformant repos as broken — and `--apply` would have written the dead marker back
  into all 24. The taxonomy is now DERIVED from `mgf.common.testing.MARKER_TIER` (canonical per
  TS-19), the check relabelled TS-25→TS-19, and `mgf-common>=0.50` floored. **This release ships
  the fix to the installed CLI** — until now the fix lived only on `main`.

### Note
34 commits had accumulated on `main` since v0.11.2 without a release. A fix that isn't released
isn't shipped. (2026-07-16: the GATE FLOOR detail above was filed under `[Unreleased]` at the
time and is restored to this release, the one that actually shipped it — `_pipeline.py` is
present at the `v0.12.0` tag.)

## [0.11.2] - 2026-07-05

### Fixed
- **`.gitleaks.toml` restore is non-destructive** (closes the PAPER-03 clobber gap
  the shape-aware drift check left open). When a consumer's file merely *lacks* a
  newly-added standard allowlist entry (e.g. template v0.1.62's `\.claude/`), the
  scaffold used to overwrite the whole file — re-adding the standard entry but wiping
  the consumer's additive carve-outs (the mgf-fastapi `test_webhooks.py` case). New
  `_drift.gitleaks_restore_text` merges the consumer's additive allowlist entries into
  the template restore; `_scaffold` calls it in place of the skip-or-overwrite guard,
  so a lagging file is brought to canonical WITHOUT losing legit per-repo carve-outs.
  Pinned in `tests/unit/test_gitleaks_shape.py`.

## [0.11.1] - 2026-07-03

### Changed
- **`release-remote` retargeted from Codeberg's package registry to the self-hosted
  devpi index** (Wave-2 P3 §3.5; founder gate P3: RETARGET — the build-once/promote flow
  and the local-index validation wave are kept verbatim: gate → build → validate →
  local-first smoke against the throwaway local index → promote). The upload endpoint
  derives from `$DEVPI_URL` (index root = the URL minus its `+simple` tail — the same
  `${DEVPI_URL%%+simple*}` derivation the CI publish step uses); the upload account is
  the index's owner segment (`magogi` for magogi/prod); the password comes from
  `$DEVPI_PASSWORD` via env (`UV_PUBLISH_PASSWORD`), never argv (SC-11). Publish
  verification is the anonymous `+simple/` listing (`_devpi_has`, PKG-05 "the index is
  the truth"), matching on the wheel-name shape `-<version>-` so `0.1.1` never
  false-positives on `0.1.10`. `_index.resolve_args(remote=True)` now steers at the
  single devpi index with **no** public-PyPI extra-index (devpi's root/pypi cache
  covers public deps); the local-first smoke (`remote=False`) is unchanged. Runbook:
  mgf-standard `docs/recipes/remote-release.md` (already devpi-shaped). NOTE: pushing
  the release tag also fires the repo's CI tag pipeline at the same index — the direct
  upload promotes the *locally-validated* wheel and either side's "already exists" is
  tolerated as idempotent.
- **`_DEVPI_INDEX` is dead — `$DEVPI_URL` resolution everywhere** (Wave-2 P3 §3.4,
  the PAPER-113 companion): generated pipelines now reference the Woodpecker
  server-level `$DEVPI_URL` variable in every renderer-emitted install (with the same
  fail-fast guard line the template fragments carry — one greppable guard shape
  fleet-wide) instead of embedding the endpoint literal. Host-side tooling resolves
  the same fact through the ONE resolver `mgf.fed._index.devpi_url()` (precedence:
  non-empty `$DEVPI_URL` → the MGF_INFRA §1 default — the only place the literal
  remains). The fleet-lock tooling (`tests/fleet/`) reads the resolver too. A devpi
  host move is now: server variable + the resolver default + the WF-13 re-render wave.
  `.woodpecker.yml` re-rendered accordingly (zero endpoint literals in the render).
- **The fleet policy has ONE namespace** (Wave-2 P3 §3.3): `load_policy` reads the
  canonical `[tool.mgf-fed.policy]` table from **mgf-standard's own** `pyproject.toml`
  first; `templates/policy.toml` remains as a DEPRECATED fallback, read iff the
  pyproject section is absent, with a one-line deprecation note on load. Same keys,
  same semantics, no gate weakened; `mgf-fed doctor` reports whichever source
  resolves. The coverage-floor default stays single-sourced in
  `_woodpecker._DEFAULT_COVERAGE_FLOOR` (85); precedence unchanged
  (`[tool.mgf-fed].coverage_floor` → `[tool.coverage.report].fail_under` → default).
  The stale `[tool.mgf.ci]` comment ghosts are swept (`[tool.mgf-fed]` is and was the
  only namespace read).
- **gitleaks drift goes shape-aware** (Wave-2 P3 §3.6; closes PAPER-03's
  false-positive class): `drift-check` compares `.gitleaks.toml` as parsed TOML shape
  instead of bytes — a consumer's ADDITIVE allowlist entries (its own fixture paths /
  extra allowlist lists) are tolerated; a removed or edited *standard* entry, and any
  non-allowlist difference (title/[extend]/rules/allowlist scalars), still fails.
  `scaffold` mirrors the rule: it never clobbers a shape-conformant consumer copy
  (which would wipe the tolerated additive entries) and still restores a mutated one
  to canonical. Both directions pinned in `tests/unit/test_gitleaks_shape.py`.

### Removed
- **Dead `skip_editable` carve-out knob** (Wave-2 P3 §3.3): parsed and assigned but
  never read — `pip-audit --skip-editable` is intrinsic to the audit command. The
  tombstone check that keeps it dead: `[tool.mgf-fed]` unknown keys (including
  `skip_editable`) now draw a stderr note from the renderer, pinned by
  `tests/unit/test_ci_carveouts.py` (which runs in single-repo CI clones too).
- `CODEBERG_INDEX` / `CODEBERG_PYPI_UPLOAD` / `CODEBERG_PKG_API` constants and the
  `_codeberg_has` REST verify — the orphaned Codeberg release path (superseded by the
  devpi retarget above; `MGF_CODEBERG_TOKEN` is no longer read by `release-remote`).

### Deprecated
- **`provision-ci-host` is now a deprecation shim → `mgf-cloud-provision ci-host`**
  (Wave-2 P0.4; Court: "migrate, one provisioner"). The in-repo implementation rendered
  the pre-Postgres SQLite compose from `deploy/woodpecker/` — a rebuild would have
  reintroduced the known-bad single-writer topology (recon H3). The verb keeps its flags
  for one deprecation cycle: it prints the notice and delegates to
  `mgf.cloud_provision.ci_host.provision_ci_host` when `mgf-cloud-provision` is
  installed, else exits 1 with install/usage instructions (no cloud action either way).
  It never forwards the stale local bundle — only an explicit `--bundle-dir`.
  `deploy/woodpecker/` (README, `docker-compose.yml`, `self-heal.sh`) now carries
  DEPRECATED headers naming the successor: the live runbook, sanitized examples,
  `ops/` self-heal scripts and `backups/` recipes all live in mgf-cloud-provision.
  Also fixed there: the propagated `PCU-`→`PCP-HOESFJV-clouds.yaml` filename typo.

### Fixed
- **AP-16 compat-matrix now covers repos whose runtime `mgf-common` lives in an
  extra, not core `dependencies`.** The floor parser (`_woodpecker._mgf_common_floor`)
  read only `[project].dependencies`, so `mgf-fed` itself — whose core is
  zero-dependency and whose real (24-import) `mgf-common>=0.49` dep sits in the
  `[cli]` extra — silently rendered **no** `mgf-common-matrix` step, excluding the
  cockpit from the AP-16 floor guard it ships to every other consumer. The parser
  now falls back to the repo's *runtime* extras (the same `.[dev,<extras>]` surface
  the matrix installs — everything except `dev`/`standards`), so an extra-only
  runtime pin is guarded. A `standards`-only conformance pin is **not** a runtime
  dep and correctly stays OFF (no floor to bound) — verified for `mgf-brand-web`,
  `mgf-fed-web`, `mgf-fedres`, `mgf-test-web` (0 runtime `mgf.common` imports).
  `mgf-fed`'s own `.woodpecker.yml` regenerates with the matrix at floor
  `mgf-common==0.49.*`. No other repo's render changes.

## [0.11.0] - 2026-07-02

### Added
- **`ping-siblings --stagger` — pace the AP-16 cornerstone fan-out** (default
  4.0s between triggers; `0` restores the old fire-all-at-once). Woodpecker mints
  a per-build clone credential against the forge for each triggered pipeline, and
  a tight ~27-in-a-row burst exhausts Codeberg's rate limit after ~a dozen: the
  tail's `clone` step then fails "could not read Username for
  'https://codeberg.org'" and never runs the compat-matrix. Observed live on the
  mgf-common 0.49.2 fan-out — the first 11 pipelines cloned, 12-27 all failed.
  Each trigger now streams as it fires. The cornerstone-ping CI step calls the
  verb with no args, so it picks up the default — no template change.

### Fixed
- **`tests/fleet/refresh_lock.py` excludes off-devpi libraries from the fleet
  co-install roster.** It filtered to `lang==python` but not to *published* libs,
  so `mgf-cloud-web` (a deliberately off-devpi local GUI tool, registered when
  mgf-cloud-app was renamed) was pulled into the full-fleet `uv` resolve →
  "mgf-cloud-web was not found in the package registry". Now probes devpi's simple
  index and keeps only libs it actually serves (honouring the roster header's
  stated "every *published* library" intent; future off-devpi tools need no
  registry flag), with a guard against writing a near-empty roster when the index
  is unreachable. Regenerated `fleet-coinstall.lock` to the current cornerstone
  (`mgf-common==0.49.2`) — restoring the `test_committed_lock_matches_cornerstone`
  gate the AP-16 ping surfaced.

## [0.10.2] - 2026-07-02

### Fixed
- **app-doctor AP-13 recognizes a dynamic `importlib.metadata` `__version__`.** A
  package whose `__init__` resolves `__version__` at runtime via
  `importlib.metadata.version(...)` with a literal fallback (e.g. `"0.0.0+unknown"`)
  was mis-read by `_read_dunder_version`, which grabbed the *fallback literal* and
  reported false AP-13 drift vs `pyproject` (seen on mgf-cloud: `0.0.0+unknown` vs
  `0.1.3`). AP-13 now detects the dynamic pattern and reports it OK ("resolved at
  runtime via importlib.metadata — no drift possible"). Cherry-picked from #58.

## [0.10.1] - 2026-07-01

### Fixed
- **AP-16 compat-matrix floor clamped to devpi's minimum (0.44).** The matrix re-pins
  `mgf-common==<floor>.*`, but devpi's oldest served mgf-common is 0.44.5 (the Phase-2
  cutover didn't backfill 0.41-0.43). A repo declaring an older floor (e.g. `>=0.41`)
  made the matrix step fail with "No matching distribution". The generator now clamps
  the matrix-tested floor UP to 0.44 (`max(pin_floor, 0.44)`); the repo's pin is left
  untouched. Un-installable older floors resolve to `>=0.44.5` in practice anyway.
- Dropped the matrix's `python -c` version diagnostic (a `': '` inside broke
  Woodpecker's YAML compiler); the non-quiet re-pin logs the resolved version instead.
  (Fragment fix shipped in mgf-standard; assertions updated here.)

## [0.10.0] - 2026-07-01

### Added
- **AP-16 federation pin-window enforcement.** The `.woodpecker.yml` generator now
  renders a `mgf-common-matrix` compat step into every repo that declares a versioned
  `mgf-common` dependency — it re-pins `mgf-common` down to the repo's own floor and
  re-runs the suite, so a real break at the floor reds CI (the upper end of the
  `>=<floor>,<1.0` window is the normal test step, resolved dynamically → no baked
  cornerstone, no drift). Gated on a parseable pin floor AND not-the-cornerstone via a
  new `mgf_common_floor` render param.
- **`mgf-fed ping-siblings`** — the AP-16 B-lite fan-out verb. After a `mgf-common`
  cornerstone release, triggers every mgf-common-consuming sibling's compat-matrix CI
  (federation roster in dev, `mgf.standard` package data in single-repo CI; skips
  cornerstone / non-Python / non-consumers; `--dry-run`; fail-closed on no token). The
  cornerstone's release pipeline runs it post-publish via the companion
  `cornerstone-ping` fragment.
- `fed-tools`: an `update` command + uv-based install; fixed the source-install index.

Companion templates — `frag-mgf-common-matrix.yml`, `frag-cornerstone-ping.yml`, the
base.yml markers, and `scripts/ping_sibling_ci.py` — ship in mgf-standard. See rule
**AP-16** / API_DESIGN §4.4.

## [0.8.0] - 2026-06-29

### Added
- **Scaffold green-on-push-1** — a freshly-scaffolded repo passes CI on its first
  push instead of needing four manual fixes (surfaced shipping the mgf-fed-web /
  mgf-test-web / mgf-fedres web tools). The renderer/scaffold half (templates land in
  mgf-standard):
  - `lint-imports` (DP-01) now renders in the generated `.woodpecker.yml` **only when
    the repo declares an import-linter contract** — a `.importlinter` file OR
    `[tool.importlinter]`. A new `has_importlinter` render param gates the
    `# <<INSERT:lint-imports>>` marker; a repo without a contract omits the line
    instead of fast-failing the lint step on "Could not read any configuration".
  - `vuln-scan` applies a fleet-default `--ignore-vuln CVE-2025-71176` (the dev/CI-only
    pytest <9.0.3 local /tmp DoS, never in any shipped wheel) on every pipeline,
    deduped with any per-repo `[tool.mgf-fed].vuln_ignore`.
  - `mgf-fed scaffold` ships the AG-18 `.githooks/prepare-commit-msg` hook (via policy
    `[exact]`) and the writer `chmod`s `.githooks/*` to 0o755 so the committed hook is
    executable on a fresh clone.

## [0.7.2] - 2026-06-26

### Changed
- **Adopt mgf-common 0.49** (pin `>=0.49,<0.50`) and retire the last three
  closed-box `_DEFERRED` private reaches (AP-14 / DP-13): `_identity._set_identity`
  + `_identity._APP_NAME` -> public `set_identity()` / `app_name()`+`current_context()`;
  `_lifecycle._GLOBAL_CONTEXT` -> `reset_context()`; `_doctor_snapshot.RUNTIME_SNAPSHOT_ENV_VAR`
  -> the public re-export. `tests/test_closed_box.py::_DEFERRED` is now empty —
  mgf.fed reaches mgf.common only through its public API.

## [0.7.1] - 2026-06-26

### Changed
- **Adopt mgf-common 0.48** — widen the `[cli]` pin to `>=0.48,<0.49` (the
  cornerstone's L3 release: the `retry` primitive + a public `io.atomic_write`).
  Part of the fleet cohort pin-walk for the 0.48 minor.
- **AP-14 (closed-box) — route `atomic_write` through the public API.** All 12
  `from mgf.common._io import atomic_write` reaches (in `cli/_catch_up.py` and
  `cli/_migrate.py`) now use the public `mgf.common.io.atomic_write` that
  mgf-common 0.48 promoted. The closed-box guard
  (`tests/test_closed_box.py`) drops its lone `_io` blessing accordingly —
  tightening the contract so no private `mgf.common._*` reach is blessed; the
  three remaining `_DEFERRED` leaks (`_identity`/`_lifecycle`/`_doctor_snapshot`)
  still await their own public mgf.common APIs.

## [0.7.0] - 2026-06-24

### Added
- **Central log shipping (Logging L2, item e).** `mgf-fed` now bootstraps log
  shipping at the top of `main()`: when `MGF_LOGSHIP_URL` and
  `MGF_LOGSHIP_INGEST_TOKEN` are set, it attaches `mgf-logship`'s `LogShipper`
  to the root logger (`app="mgf-fed"`), so every invocation's logs reach the
  federation log host (openobserve-prod) over the NDJSON `/ingest` path. When
  shipping is enabled it also runs `mgf.common.bootstrap()` so records carry
  app/trace context (resolves the `current_context() before bootstrap` warning).
  Fully opt-in and guarded — unset env, or `mgf-logship` not installed, is a
  silent no-op, so the zero-dep orchestration core is unchanged. New private
  `mgf.fed._logging` module; `mgf-logship>=0.1.6` added to the `[cli]` extra.
  Verified end-to-end against the live collector (record + `app_version` land in
  OpenObserve).

## [0.6.0] - 2026-06-19

### Added
- **`mgf-fed start` — the single federation front-door.** One command, one
  consistent agent message for every onboarding case. It is a print-only
  dispatcher over the four existing prompts (it never duplicates their bodies,
  resolved from `mgf-standard`): auto-detects when the repo is already a
  registry member (`[project].name` in `mgf.standard.libraries()`) and prints
  the **sync** prompt; otherwise prints a scenario menu listing every mode with
  a detection hint (empty vs has-code, consumer vs not). `--mode
  {new|onboard|sync|adopt}` forces a mode, `--list` always prints the menu,
  `--path` points detection at a directory other than the cwd. Print-only by
  contract — it emits a prompt or a menu and never touches the filesystem.

### Fixed
- `mgf.fed.__version__` was stuck at `0.4.0` while the distribution shipped
  `0.5.0`; both now track the package version.

## [0.5.0] - 2026-06-16

### Added
- **The end-user / governance CLI, moved from `mgf-common`** (federation reorg
  Wave 3). `pip install 'mgf-fed[cli]'` adds `mgf-fed <verb>` for the former
  `mgf-common` subcommands: `version` / `bundle` / `explain` / `validate` /
  `migrate` / `bench` / `logs` / `standards` / `federation` / `design` /
  `init-project` / `catch-up` / `sync-prompt` / `new-project-prompt` /
  `adopt-prompt` / `onboard-prompt` / `agents` / `federation-sync` / `test`,
  plus **`app-doctor`** (was `mgf-common doctor`) and **`feedback-howto`** (was
  `mgf-common feedback`) — renamed so they don't shadow mgf-fed's native fleet
  `doctor` (self-check) and `feedback` (FB-05 relay). The commands live in the
  new `mgf.fed.cli` package and self-register into mgf-common's subcommand
  registry; `__main__` merges them into the native fleet parser.
- `[cli]` extra (`mgf-common>=0.47`, `mgf-standard`, `mgf-test-supervisor`).
  The zero-dep orchestration core (status / release / drift / …) still runs
  without it — every cli handler is imported lazily and the parser degrades
  gracefully when the extra is absent.

### Changed
- `[standards]` extra is now a thin alias for `[cli]` (back-compat).
- The `[cli]` extra requires `mgf-common>=0.47,<0.48` (the pure-library release
  that shed this CLI).

## [0.4.0] - 2026-06-14

### Added
- **Woodpecker pipeline generator** — `.woodpecker.yml` is generated per-repo from
  `mgf-standard/templates/woodpecker/` (`_woodpecker.render_woodpecker`); `scaffold` writes it,
  `drift-check` enforces generation-currency with a `[exempt].woodpecker` name-list fallback for
  bespoke repos. The CI-unification engine.

## [0.18.1] - 2026-07-31

### Fixed
- `AUDIT.json` excluded from the release-currency source question. The audit's own
  committed artifact is a repo-root governance file no wheel ships; leaving it out
  made the gate report the repo that stores its results — one hour after the same
  gate taught the same lesson about `AGENTS.md`.

## [0.9.0] - 2026-06-29

### Added
- **`fedres check` freshness gate in the woodpecker template (D-GEN).** The
  renderer now splices a standalone `fedres-check` step into a repo's
  `.woodpecker.yml` when (and only when) the repo carries
  `fed-resources/source/project.toml` (new `has_fed_resources` gate, mirroring
  `lint-imports`). The step installs `mgf-fedres` + `mgf-common[standards]` and runs
  `fedres check` — regenerate→byte-identical, nonzero on drift. The Class-P
  projectors read the registry from the mgf.standard package data, so the gate runs
  in a single-repo CI checkout (no sibling mgf-standard needed).

## [0.3.0] - 2026-06-13

### Added
- `mgf-fed feedback` — relay cross-repo feedback outboxes into target inboxes (FB-05). `--check`
  (default) scans every member's `pending-feedback/*.md` and exits non-zero on any un-relayed paper
  (the no-silent-rot gate); `--relay [--target R] [--dry-run]` assigns the target's next `PAPER-NN`,
  renders the FB-02 inbox entry into the target `FEEDBACK.md`, moves the source paper to
  `pending-feedback/relayed/`, and prints the reviewed PR commands. Idempotent; implements the
  mgf-standard `feedback-relay.md` recipe.

## [0.2.0] - 2026-06-11

### Added
- `mgf-fed doctor` — self-diagnostic (OP-02); rich `--version` (OP-01); top-level error handler (EH-04).
- Full ten-doc baseline (AGENTS/SECURITY/CONTRIBUTING/STARTHERE/FEEDBACK/FEDERATION + docs/claude/CLAUDE.md + PUBLIC_API.md) + docs buckets.
- Quality infra: coverage gate (>=80%), import-linter contract (WF-02), 13-marker test taxonomy + smoke layer, pytest-timeout, mgf-test-supervisor adoption ([supervisor] extra), [project.urls].

### Changed
- §Z conformance audit: mgf-fed now 122 ✅ / 0 ⏳ / 96 N/A — 100% of applicable MUSTs (was 80%).

## [0.1.0] - 2026-06-10

### Added
- Initial release — the federation orchestrator skeleton (Phase 0).
- `mgf-fed status` — read-only federation tracking matrix (per-repo version, conformance %,
  CI / import-linter / PUBLIC_API / ledger / `[standards]` presence) → `~/.mgf/MGF_FED_STATUS.json`.
- `mgf-fed drift-check` — verify each repo against the canonical scaffolding policy
  (`mgf-standard/templates/policy.toml`): present-files, byte-exact shared files, required
  `.woodpecker` steps, and pyproject gate-policy (incl. the ruff format-stability pin).
- `mgf-fed local-index` — inspect the local package index (`~/.mgf/local-index/`); the
  `local → Codeberg → PyPI` resolution mechanism for the upcoming local-release flow.
- Roadmap subcommands stubbed (`gates`, `local-ci`, `release-local`, `release-remote`,
  `scaffold`, `registry`) so `--help` documents the plan.
