# Changelog

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

## [Unreleased]

## [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.

## [Unreleased]

## [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.
