# Changelog

All notable changes to `mgf-fedres` are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2026-07-17

### Added

- **CIR-26 — a supported family-adapter seam: blessed slot overrides +
  `MGF_FEDRES_PROJECTORS`.** A second family can now point the Class-P projector
  slots (`architecture` / `federation`) at its own registry projectors through the
  **stock CLI**, with no wrapper script and no fragile import-order dance:

  - **`register_projector(name, fn)` is REPLACE-capable by contract.** Registering a
    name already bound — *including* a built-in slot — replaces it, last-write-wins.
    Overriding a built-in is supported behavior, not an accident, pinned by
    `test_register_projector_replaces_a_builtin_slot`.
  - **`MGF_FEDRES_PROJECTORS`** — a colon-separated list of importable module paths,
    imported by the CLI *after* the built-ins register and *before* dispatch, so each
    module's import-time `register_projector(...)` calls win. Dependency-free (stdlib
    `importlib`). A module that cannot be imported is a loud, named failure — never a
    silent fall-back to the built-ins.

  The **Sooth family** is the first consumer: its adapters read `SOOTH_LIBRARIES.json`
  and project a `ring-*`-layered graph, reaching the stock CLI via this seam instead of
  the old "override projectors in-process after import, dispatch through a wrapper"
  pattern (`soothfast/tools/fedres.py`) that existed only because "mgf-fedres has no
  plugin-path mechanism". The end-to-end sooth shape is now pinned through public API
  only by `test_sooth_shape_adapter_end_to_end_through_the_cli_env_hook` — the contract
  that a future mgf-fedres change cannot silently break the sooth adapter pattern.

  MINOR bump: additive public surface (a new supported override contract + env hook),
  no behavior change when `MGF_FEDRES_PROJECTORS` is unset. Cites the 2026-07-17 CI
  Support Review, finding CIR-26.

## [0.3.0] - 2026-07-17

### Changed
- **BREAKING (regenerate required): `architecture` is now scoped to THE PROJECT by
  default** — this project plus its transitive sibling deps — instead of a copy of
  the whole roster. New `[projectors.architecture].scope`: `"project"` (default) or
  `"federation"` (the roster owner, `mgf-standard`).

  **Why — the coupling this closes.** The whole-federation graph is ONE global fact,
  and emitting it into every member byte-pinned a copy of the roster into ~35 repos.
  Those repos read the roster from the **installed** `mgf-standard` (package data), so
  the input to a byte-pinned artifact was a **floating dependency**: publishing the
  registry — even adding a single leaf member nothing depends on — turned every one of
  those repos' `fedres check` **RED through no change of their own**, and no registry
  release could avoid it. Proven by bisect: `mgf-http` / `mgf-auth` / `mgf-vm-core`
  were `IN_SYNC` against mgf-standard 0.1.62 and `STALE` against 0.1.63.

  Scoping makes churn proportional to the change: a leaf nobody depends on touches
  **nobody**, and a repo's file moves only when its own architecture actually does. It
  also matches what the file claims to be — `architecture.json` lives under a
  project's `fed-resources/generated/` and is keyed by that project's id, so the whole
  foundation was never the honest answer to "this project's architecture". It brings
  the projector in line with its sibling `federation` projector, which was always
  correctly scoped to "this project's coupling".

  **`mgf-standard` keeps the full graph** via `scope = "federation"`, and projects it
  from its own repo-local `MGF_LIBRARIES.json` — so the one place the roster graph
  lives has no external input to drift against.

  **Migration:** run `fedres generate` and commit. A member's `architecture.json` will
  shrink to its own dependency closure. This is the LAST roster-wide regeneration —
  that is the point.

## [0.2.0] - 2026-07-14

### Changed
- **Released what accumulated on `main` since v0.1.1.** Ships the Apache-2.0 relicence (the published v0.1.1 wheel still carried `License: MIT` with no `NOTICE`) plus the fixes and pin updates merged since. A fix that isn't released isn't shipped; `mgf/tools/verify_release_currency.py` (now devpi-aware) flags this class.

## [Unreleased]

### Added

- **PAPER-01 — the `RegistryAdapter` seam: a second family registers, not forks.**
  The reference projectors no longer bake in the MGF registry shape: a
  `RegistryAdapter` (`(provider, cfg) -> Sequence[RegistryEntry]`) normalizes an
  arbitrary family registry to the `{repo, lang, deps}` view the projector
  bodies consume. The MGF-shaped loader (top-level `libraries`, `mgf.standard`
  package-data fallback) is now the default-registered `mgf` adapter — nothing
  changes for mgf-standard — and a second family calls
  `register_registry_adapter(...)` + `[projectors.*] registry_adapter = "<name>"`
  instead of copying `projectors.py`. Unknown adapter names refuse loudly with
  the registered list. Also fixed the README Status section, which still said
  "Planning / pre-implementation" about an implemented, tested engine.
- **PAPER-04 — the supervisor's full outcome vocabulary is native.**
  `test_status_value` (and `TEST_STATUS_VALUES`) widened additively with
  `crashed`, `hung`, `quarantined`, so `test_run_record.result` /
  `test_catalog.last_status` carry the mgf-test-supervisor outcome verbatim
  instead of a flattened `error`/`skipped` projection; `unknown` remains the
  loud fallback. Additive enum ⇒ every previously-valid payload stays valid
  (schema MINOR; `schema_version` stays `0.1`).
- **`fedres session-close`** — the foundation-wide Session-Close Standard as a command.
  The end-of-session hygiene gate (`--check`): verifies the mechanically-checkable MUSTs
  (durable state committed, branch not silently ahead of upstream, no `fed-resources` live
  session left open) and exits non-zero on any failure — non-blocking-but-loud. Plus
  `--init` (a state-at-exit record scaffold, pre-filled from git + the live channel, so an
  agent edits rather than authors) and `--doc` (prints the canonical `SESSION_CLOSE.md`).
  Cross-family by construction: both fleets reach it through this one `mgf.fedres` entry
  point (Sooth via its `tools/fedres.py` wrapper — zero Sooth-side edits), and the canonical
  standard ships as package data so it is byte-identical in both families.

### In progress — M1 (engine + CLI)

- The **sync engine**: canonical-JSON serialization, the per-file `sha256`
  manifest over `generated/**`, the shallow (manifest) + deep
  (`regenerate → byte-identical`, non-mutating) check, and the freshness gate.
- The **`SourceProvider` seam** (`DiskProvider` now; `PackageProvider` stub later).
- The **D-GEN framework** — the `source/ → generated/` projection contract.
- The **live NDJSON** reader/writer + the **run-record** writer (gated,
  reproduce-exempt, integrity-hashed).
- The **`fedres` CLI** — `status` / `check` / `generate` / `run --trace` / `tail` /
  `session-close`.

> Not yet released. See [docs/planning/PLAN.md](docs/planning/PLAN.md) (phase A3).

## [0.1.0] — M0, the fed-resources contract

> Initial pre-alpha cut (Development Status :: 2 — Pre-Alpha); not yet tagged or
> published.

### Added

- The **schema set** (`schema_version 0.1`) — one versioned, symmetric authority
  across the four governance classes: gated state (`manifest`, `status`,
  `roadmap`, `architecture`, `focus`, `suggestions`, `federation`, `test_catalog`),
  gated/reproduce-exempt (`run_record`, `test_run_record`), authored (`feedback`,
  `response`), and the live/ephemeral wire contracts (`live_event_fed`,
  `live_event_test`, `test_status`) carrying a compact `"v"`.
- **`fedres-validate`** — the fail-closed validator: an unreadable file, malformed
  JSON, an unknown `schema_version`/`v`, or any schema violation is a nonzero exit;
  `.ndjson` streams are validated line by line.
- The **version-aware reader** (`read_file` / `read_payload` / `ReadResult`) —
  honest-absence loading: a missing file is `UNKNOWN`, an unparseable /
  unknown-version / schema-violating payload is `UNREADABLE` — never a guess, never
  stale-shown-as-current.
- The **closed state vocabularies** (`SyncState`, the test-status set) and the
  supported contract versions, mirrored from the schema authority and pinned in
  lock-step by a test.
- Schema-discrimination (`identify`): documents self-describe via
  `{schema_version, kind}`; live events carry `{"v": ...}` and are discriminated by
  their `session` (fed) vs `run_id` (test) field.

[Unreleased]: https://codeberg.org/magogi-admin/mgf-fedres/src/branch/main
[0.4.0]: https://codeberg.org/magogi-admin/mgf-fedres/src/tag/v0.4.0
[0.1.0]: https://codeberg.org/magogi-admin/mgf-fedres/src/branch/main/CHANGELOG.md
