# v0.18.0 — Cutover guide

> **Audience:** consumer agents (and humans relaying for them)
> upgrading to `mgf-common` 0.18.0.
> **What this is:** the canonical migration document for this
> release. Per [`RELEASING.md`](../standards/RELEASING.md) **REL-09**
> + the cutover-guide pattern shipped in PAPER-21, individual
> per-PAPER notification paragraphs are now pointers into this
> guide. Read the §s for the entries you care about.

This guide is structured one § per closed FEEDBACK entry.
Entries are ordered by likely impact on the consumer
(architectural first, then aspirational acks).

---

## At-a-glance

| Entry | Severity | What landed | Code or docs? |
|---|---|---|---|
| [HTTP-01](#http-01--httperror-category) | 🟡 | New `HttpError` category in `mgf.common.exceptions` (12 names) | code |
| [CONFIG-01](#config-01--framework-native-config-carve-out) | 🟡 | Framework-native carve-out section in `CONFIGURATION.md`; per-rule applicability annotations | docs |
| [LOG-01](#log-01--cloud-native-logging-recognised) | 🟡 | LG-06 revised to recognise stdout/stderr aggregation as first-class; new recipe `cloud-native-logging.md` | docs |
| [LOG-02](#log-02--applicability-scope-on-lg--rules) | 🟢 | Per-rule `applies_to:` scope on every LG-* rule; per-kind compliance matrix at top of `LOGGING.md` | docs |
| [PAPER-21](#paper-21--fastapi-template-closed-box-leak-fix) | 🔴 | One-line template fix removing `from mgf.common._lifecycle import bootstrap` leak; regression guard pins all kind templates | code |
| [ASPIRE-02](#aspire-02--second-consumer-arrival-acknowledged) | 💭 | Strategic-Review P1 marked achieved; PAPER-06 unblocked | docs |
| Cutover-guide pattern | maintainer-initiated | This document; `RELEASING.md` REL-09 updated to reference cutover guides as the canonical migration source | docs |

**Pin bump:** `mgf-common = ">=0.18,<0.19"` per the 0.x window
discipline (one minor at a time; AP-03).

**Public-Python-surface change:** 12 new names in
`mgf.common.exceptions.__all__` (HTTP-01 hierarchy). Additive —
existing `except AppError:` / category catches keep working
unchanged. New names stable tier `experimental` per AP-11.

**No deprecation cycles introduced.** Nothing existing was
renamed or removed.

---

## HTTP-01 — `HttpError` category

The eighth category in the typed-exception taxonomy. Closes the
gap that web/API consumers had no natural home for HTTP-status-
coded exceptions in the original systems-management-domain seven
(`ConfigError`, `ProviderError`, `OperationError`, `GuestError`,
`HostEnvironmentError`, `VaultError`, `SchedulerError`).

**What's new:**

```python
from mgf.common.exceptions import (
    HttpError,                      # base (default 500)
    HttpClientError,                # 4xx base (default 400)
    HttpServerError,                # 5xx base (default 500)
    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. Consumer
subclasses inherit it (and may override).

**Consumer pattern:**

```python
# project/exceptions.py
from mgf.common.exceptions import HttpNotFound, HttpClientError

class RoomNotFoundError(HttpNotFound):
    """Project-specific 404."""
    # Inherits http_status = 404

class RoomArchivedError(HttpGone):
    """Project-specific 410."""
    # Inherits http_status = 410

# Framework integration (Django middleware example):
def app_error_middleware(request, exception):
    if isinstance(exception, HttpError):
        return HttpResponse(
            json.dumps({"error": str(exception)}),
            status=exception.http_status,
            content_type="application/json",
        )
    raise exception
```

**Three-level category-catch property:**

```python
try:
    do_request_handling()
except HttpServerError:    # all 5xx
    ...
except HttpClientError:    # all 4xx
    ...
except HttpError:          # everything HTTP-shaped
    ...
except AppError:           # everything in the typed hierarchy
    ...
```

**What to migrate:**

If you have an HTTP-shaped exception hierarchy today (the
`inthewords/apps/core/exceptions.py` shape), the migration is to
re-base your project's leaves on the new category:

```python
# Before — your project's bespoke `AppError`-rooted hierarchy:
class AppError(Exception):
    http_status = 400

class NotFoundError(AppError):
    http_status = 404

# After — re-base on mgf-common's category, drop the http_status
# duplication, get the catch-by-category property for free:
from mgf.common.exceptions import HttpNotFound

class NotFoundError(HttpNotFound):
    pass
```

This is structural. There's no auto-migration; touch your
exception classes when you're ready. Existing code paths keep
working unchanged.

**Distinct from provider-domain:** `HttpNotFound` (web) vs
`ResourceNotFoundError` (provider / SDK / hardware boundary)
are different categories. They both subclass `AppError`; they
do NOT subclass each other.

---

## CONFIG-01 — Framework-native config carve-out

`docs/standards/CONFIGURATION.md` adds a new sub-section
recognising framework-native settings (Django's
`from django.conf import settings`, Rails-equivalent patterns)
as a first-class shape.

**What's new in `CONFIGURATION.md`:**

- A "Framework-native config" sub-section that explicitly
  exempts CF-01 (no env reads outside config), CF-04 (atomic
  on-disk writes), CF-08 (no module-global singletons), and
  CF-09 (`--config <path>` CLI flag) for consumers whose
  framework owns the config schema.
- Per-CF rule applicability annotations (`applies_to: ["all"]`
  vs `applies_to: ["consumer-owned-config"]`).
- A worked example for the `inthewords` Django shape (which
  CF-* rules apply, which are N/A, with the rationale per
  rule).

**What to do (Django consumers):**

1. Re-read `mgf-common standards config`. The carve-out section
   names which CF-* rules apply to your shape.
2. If you've been declaring CF-* "not applicable" via
   project-side exception comments, replace those with
   pointers to the carve-out section.
3. Continue using `from django.conf import settings`. The
   standard now blesses it.

**What to do (consumer-owned-config consumers):**

Nothing — the existing CF-* rules apply unchanged. The
carve-out is additive recognition of an alternative shape.

---

## LOG-01 — Cloud-native logging recognised

`docs/standards/LOGGING.md` revises **LG-06** to recognise the
12-factor / cloud-native shape (stdout/stderr → platform
aggregator) as a first-class option alongside the desktop-/
server-app rotating-file shape.

**What's new in `LOGGING.md`:**

- LG-06's text is parameterised by deployment topology:
  - Rotating local file (desktop / single-server, no platform
    aggregator).
  - Process stdout/stderr (cloud-native / container; the
    runtime captures + forwards to the aggregator).
  - Remote sink (OTLP / syslog / HTTP webhook; opt-in).
- "Console only, no aggregation" is non-compliant in
  production (regardless of branch).
- New recipe at `docs/public/recipes/cloud-native-logging.md`
  — covers stdout-only `MgfSettings`, JSON formatter choice,
  structured-log invariants the platform aggregator needs,
  and the deployment-time check.

**What to do (cloud-native consumers — Kubernetes, Docker,
Heroku, Cloud Run, Fly.io, etc.):**

1. Re-read `mgf-common standards logging` — the LG-06 wording
   now blesses your shape.
2. Configure `MgfSettings` so `logging.file=None` AND your
   deployment captures stdout. The runtime IS your rotating
   file sink.
3. Verify your structured-log lines parse cleanly in the
   platform aggregator (Grafana, Datadog, CloudWatch).

**What to do (desktop / single-server consumers):**

Nothing — the rotating-file branch keeps the existing
prescription verbatim. The carve-out is additive.

---

## LOG-02 — Applicability scope on LG-* rules

Closes the discoverability papercut: standards docs presented
LG-01..LG-14 as a flat list, with LG-07 (GUI log-viewer
dialog) treated identically to LG-01 (root-level structured
logging) despite the former being scoped to GUI apps only.

**What's new in `LOGGING.md`:**

- An "Applicability" sub-row on every LG-* rule with a
  machine-readable `applies_to` field — values from the set
  `{cli, fastapi, django, library, service, gui-app, all}`
  (matching the `init-project` `_KINDS` set + `gui-app` for
  the desktop case).
- A "Per-kind compliance matrix" table at the top of
  `LOGGING.md` listing which rules apply to which kinds.
- LG-07 is now annotated `applies_to: ["gui-app"]`. catch-up
  audits + future linters know not to ding non-GUI consumers
  for missing GUI dialogs.

**What to do:**

Nothing structural — this is metadata that `catch-up` and
audit tooling now respect. If you previously got a warning
about LG-07 on a non-GUI project, it's gone.

---

## ASPIRE-02 — Second consumer arrival acknowledged

ASPIRE-02 closes by recording three things:

1. **Strategic-Review Priority 1 — `Get a second, independent
   consumer` — is achieved.** `inthewords` is the second
   independent consumer; `PlasmaMapper` is the third.
   `STRATEGIC_REVIEW.md` updated to reflect this.

2. **PAPER-06 unblocks.** The reference-consumer restructure
   (deferred specifically waiting for a second consumer) can
   now happen — `docs/reference-consumers/Project1.md`
   (VManager) + `docs/reference-consumers/Project2.md`
   (inthewords) split. Tracked as a follow-up paper for a
   future release.

3. **PAPER-10's reopen criterion 2 receives evidence —
   non-pain.** `inthewords` reports they don't need
   entry-point auto-discovery (Django `app.ready()` is the
   equivalent dispatch mechanism). PAPER-10 stays DEFERRED;
   the entry now lists this evidence inline.

**What to do:**

Nothing — this is process / strategic-review accounting. But
it does mean the maintainer is no longer waiting on a "second
consumer" gate for several deferred items.

---

## PAPER-21 — FastAPI template closed-box-leak fix

The new `kind: fastapi` template that shipped in v0.17.2 (closing
PAPER-17) wrote a quick-start code block into the auto-managed
section of `docs/CLAUDE.md` whose imports included:

```python
from mgf.common._lifecycle import bootstrap
```

`mgf.common._lifecycle` is a private module. The public name is
`mgf.common.bootstrap`. The template was teaching consumers to
import from the private path — the same pattern the catch-up
closed-box-leak audit (PAPER-08, shipped v0.17.0) flags on
consumer src/. PlasmaMapper caught the audit-and-template
asymmetry within hours of v0.17.2 publishing.

**Severity 🔴 Blocker:** every new `fastapi` consumer's first
Claude session loaded the leak into context.

**What's fixed:**

```diff
- from mgf.common._lifecycle import bootstrap
+ from mgf.common import bootstrap
```

(One line in `_QUICK_START_BLOCKS["fastapi"]` in
`src/mgf/common/cli/_init_project.py`.)

**Regression guard:** new test class
`TestQuickStartBlocksNoClosedBoxLeaks` in
`tests/unit/cli/test_init_project.py` walks every kind
template's rendered output through the same `_is_private_module`
predicate the catch-up audit uses. Five kinds × the predicate;
plus a PAPER-21-specific assertion pinning the canonical public-
import form. The audit and the templates can't drift again.

**What to do (FastAPI consumers who scaffolded against v0.17.2):**

1. Bump pin to `>=0.18,<0.19`.
2. Re-run `mgf-common catch-up --apply`. The auto-managed
   block in `docs/CLAUDE.md` refreshes with the corrected
   import.
3. If you copy-pasted the v0.17.2 quick-start block into your
   actual `src/<project>/app.py`, replace
   `from mgf.common._lifecycle import bootstrap` →
   `from mgf.common import bootstrap`. Re-run `mgf-common
   catch-up` — the closed-box-leak audit row now reads ✅.

**What to do (other kinds):**

Nothing — the regression guard verified `cli`, `django`,
`library`, `service` templates were already clean. Only
`fastapi` had the leak.

---

## Cross-references

- [`CHANGELOG.md`](../../CHANGELOG.md) — the audit trail of
  what shipped (this doc is the consumer-action artifact;
  CHANGELOG is the historical record).
- [`FEEDBACK.md`](../../FEEDBACK.md) — the design records
  for HTTP-01, CONFIG-01, LOG-01, LOG-02, ASPIRE-02, PAPER-21
  (each entry's retrospective points back here).
- [`RELEASING.md`](../standards/RELEASING.md) §7 — the
  cutover-guide pattern (REL-09 delivery mechanism).
- [`STRATEGIC_REVIEW.md`](../internal/STRATEGIC_REVIEW.md)
  Priority 1 — now achieved (ASPIRE-02 closure).
