# Pre-release check — federation v1.0 readiness

> **Status:** working document. First pass 2026-05-17, immediately
> after the federation-wide P2 sweep + mgf-common 0.34.0 publish.
> Captures everything I'd want the maintainer to see before pulling
> the v1.0 trigger.
> **Audience:** the maintainer + future Claude sessions driving
> v1.0 release work.
> **Companion docs:**
> [`federation_roadmap.md`](federation_roadmap.md) (the split plan
> + quality gates),
> [`strategic_review.md`](strategic_review.md) (org/community
> strategy), [`STANDARDS_COMPLIANCE.md`](../STANDARDS_COMPLIANCE.md)
> (rolling audit record).
> **Scope:** every Magogi federation project — mgf-common + 7
> siblings (mgf-alembic, mgf-apiprobe, mgf-django, mgf-fastapi,
> mgf-http, mgf-sqlalchemy, mgf-website-template).

---

## §1 TL;DR

The federation is **closer to v1.0 than it might feel** but the
0.x train hasn't fully arrived at the station. Six known
blockers, the largest being:

1. **6 of 7 siblings have unreleased work on PyPI** (11–14 commits
   since each sibling's last tag — all the P1 + P2 sweep landed
   in-tree, not on PyPI).
2. **mgf-django has NEVER been published to PyPI** — first publish
   still needed.
3. **All sibling `mgf-common` pyproject pins are now stale**
   (`>=0.33,<0.34` vs released 0.34.0).
4. **mgf-apiprobe has a known version-drift test failure**
   (`_version.py` 0.2.1 vs `pyproject.toml` 0.3.0).
5. **AP-09 stability promotion (`experimental` → `stable`)** has
   never been executed federation-wide; sibling coverage is uneven.
6. **v1.0 cutover guide** doesn't exist — federation roadmap calls
   for one spanning all siblings.

The good news: the heavy lifts (P0+P1 reliability/security/perf)
are all closed federation-wide. What remains is mostly
**release-engineering hygiene**.

Estimated effort to clear blockers: **1–2 focused sessions**.

---

## §2 Methodology — what I checked

1. **Per-sibling tracker counts** (RA / SH / PC) — open vs closed
   vs deferred-to-v1.1+.
2. **PyPI state** vs local `pyproject.toml` version per sibling.
3. **Commits since last tag** per sibling.
4. **Sibling `mgf-common` pin shape** in each `pyproject.toml`.
5. **CHANGELOG `[Unreleased]` section** per sibling.
6. **PUBLIC_API.md / .json sync state.**
7. **Test pass rate** federation-wide.
8. **`mgf-common/FEEDBACK.md` open papers** (§2 open feedback).
9. **AP-11 stability-tier coverage** in each sibling's
   `PUBLIC_API.md` (proxy: count of `experimental` / `stable` /
   `deprecated` mentions).
10. **`docs/inprogress/` directories** for each project — what's
    still WIP.
11. **TODO / FIXME / XXX scan** in every `src/` (clean across the
    federation).
12. **mgf-common ruff state** in `src/` — pre-existing noise.
13. **mgf-common ruff/mypy/pytest** on master.
14. **Memory entries** for known-deferred work + watch items.

---

## §3 Findings

Three severity tiers: **BLOCKER** (must do before v1.0 cut),
**IMPORTANT** (should do — quality + correctness), **NICE**
(polish — could ship at v1.0 or in a follow-up).

### §3.1 BLOCKERS (B-1 .. B-7)

| # | Project | Item | Effort | Why |
|---|---|---|---|---|
| **B-1** | mgf-django | **First-ever PyPI publish.** Local at 0.2.0; PyPI returns nothing for `mgf-django`. Likely never registered. | S | Without this, the federation has a missing limb at v1.0. Will hit PyPI's "new-project rate-limit" (~24h cooldown per [`reference_pypi_new_project_rate_limit`](../../../.claude/projects/-home-bassam-PycharmProjects-mgf-common/memory/reference_pypi_new_project_rate_limit.md)) if other new-project creates happened recently. |
| **B-2** | mgf-apiprobe | **Fix version drift.** `src/mgf/apiprobe/_version.py` says `0.2.1`; `pyproject.toml` says `0.3.0`. Failing test `test_version_format` since the P1 cycle. | XS | Pre-existing breakage; surfaces in every test run since the P1 sweep. Single-file fix. |
| **B-3** | mgf-alembic, mgf-apiprobe, mgf-django, mgf-fastapi, mgf-http, mgf-sqlalchemy | **Release the accumulated 0.x work to PyPI.** Each has 11–14 commits since its last tag — all the P1 + P2 sweep work I did this session is in-tree but not shipped. | M total | Quality gate "PyPI verification" in [`federation_roadmap.md`](federation_roadmap.md); consumers `pip install` the published wheel, not the git HEAD. |
| **B-4** | All 6 Python siblings | **Bump `mgf-common` pin from `>=0.33,<0.34` to `>=0.34,<0.35`** (or to the chosen v1.0 pin shape) in each `pyproject.toml`. | XS each | Current pins reject the released 0.34.0. Any consumer asking for `mgf-common==0.34.0` + any sibling fails the resolver. |
| **B-5** | All 6 Python siblings | **Update `[Unreleased]` CHANGELOG section** with the actual changes (P1 reliability/security/perf, P2 sweep, v2.6 shape declaration). Each currently says "Nothing yet" despite real work. | XS each | Consumers read CHANGELOG to decide upgrade. Federation quality gate REL-09 expects retrospective notes. |
| **B-6** | Federation-wide | **AP-09 stability promotion sweep.** [`API_DESIGN.md`](../standards/API_DESIGN.md) AP-11 mandates per-name stability tiers; [`federation_roadmap.md`](federation_roadmap.md) §v1.0 calls for promoting all `experimental` → `stable`. Coverage today: mgf-common 217 mentions, mgf-apiprobe 86, mgf-fastapi 26, mgf-alembic 5, mgf-sqlalchemy 7, mgf-http 8, mgf-django 9. | M | Without this, v1.0 doesn't mean what the standard says it means — the deprecation cycle (AP-12) can't apply against names that never declared `stable`. |
| **B-7** | mgf-common | **Write the v1.0 cutover guide** (`docs/cutover/v1.0.0.md`). [`federation_roadmap.md`](federation_roadmap.md) §v1.0 explicitly calls for "one v1.0 cutover guide that spans all siblings." | S | The federation-wide "what consumers must do at the v1.0 cut" doc. Locks the migration story before the release goes out. |

### §3.2 IMPORTANT (I-1 .. I-9) — quality + correctness

| # | Project | Item | Effort | Why |
|---|---|---|---|---|
| **I-1** | Federation-wide | **Federation-wide SBOM + `pip-audit` CI workflow.** Deferred-to-v1.1+ in EVERY sibling's SH tracker (mgf_alembic SH-05, mgf_apiprobe SH-07, mgf_django SH-04, mgf_fastapi SH-07, mgf_http SH-06, mgf_sqlalchemy SH-05). | S–M | The federation's largest single gap. One cross-sibling workflow closes 6 trackers. SC-10 + SC-18 already mandate the discipline — only the CI shape is missing. |
| **I-2** | All siblings | **Each sibling's `PUBLIC_API.md` + `PUBLIC_API.json` pin-test** must pass at the release commit. mgf-common's drift-test pattern (regenerate before commit) is the model. | XS each | I verified this on mgf-common during the 0.34.0 cut — it caught the `__version__` drift before publish. Equivalent gate should run on every sibling. |
| **I-3** | mgf-common | **`coverage.md` claims 1445 tests at v0.24.0; today is 1413.** [`coverage.md`](coverage.md) notes the layer-summary is "as of v0.24.0". Document the post-extraction baseline. | XS | Each extraction (v0.28..v0.31) moved tests out to siblings. The current count is correct but the per-layer table is stale. |
| **I-4** | mgf-common | **Pre-existing ruff noise in `src/`** — 29 errors. Mix of `RUF100` (unused noqa), `N818` (intentional `Http*` exception class names), `F401` (unused imports), `SIM110`, `TC003`. Either ignore explicitly via config OR fix in a cleanup commit. | XS–S | Not strictly blocking, but `ruff check` is a quality gate. Currently masked by the test suite passing — anyone running `ruff` cold sees noise. |
| **I-5** | mgf-fastapi | **60-second test suite** (vs 1–14s for every other sibling). Worth a quick profile pass — likely `run_test_app` uvicorn startup dominating. | S | Slow tests slow the whole release cycle. The PC-07 deferral was the right call for v1.0; a one-shot profile pass might pluck the easy wins. |
| **I-6** | mgf-common | **Open FEEDBACK papers (`§2`):** PAPER-31..34 (recent PlasmaMapper audit), ASPIRE-04 (codify non-adoption — partially shipped, README done; per-recipe promotion ongoing), ASPIRE-06 (federation architecture — closes WHEN v1.0 ships), PAPER-10 (entry-point plugin auto-discovery — marked DEFERRED). | XS–S | Skim each; decide ship-or-defer-explicitly before v1.0 lock. The OPEN status accumulates; v1.0 is the right cut point. |
| **I-7** | Each sibling | **Run `mgf-common catch-up` against each sibling** — federation quality gate ("`mgf-common catch-up` clean"). Catches private-import leaks (`from mgf.common._*`) per PAPER-08. | XS each | Builds confidence the siblings are clean-box consumers of mgf-common's public API. |
| **I-8** | Each sibling | **Import-linter inter-package coupling contract** — federation quality gate. Ensures sibling imports `mgf.common` only through public names. | XS–S each | One per-sibling `importlinter.toml` (or pyproject section) + CI hookup. |
| **I-9** | mgf-website-template | **Confirm v1.0 boundary semantics for the template.** Not a PyPI package — it ships via `cookiecutter gh:codeberg.org/...`. What does "v1.0" mean for it: a tag, a CHANGELOG entry, a `README --checkout v1.0.0` recommendation? Memory says `--checkout v1.0.0` is the recommended shape; lock the tag. | XS | The template doesn't fit the PyPI-publish quality gate; needs its own gate-equivalent. |

### §3.3 NICE (N-1 .. N-10) — polish

| # | Project | Item | Effort | Why |
|---|---|---|---|---|
| **N-1** | Federation-wide | **Smoke test on PyPI install:** fresh venv + `pip install mgf-<sibling>` + `python -c "import mgf.<sibling>"` for each released sibling. Federation quality gate. | XS each | Catches packaging mistakes invisible to `pytest` (missing files, wrong `__init__.py`, namespace-package collisions). |
| **N-2** | Federation-wide | **Consumer notification (REL-09)** — a short doc / FEEDBACK retrospective listing affected consumers (PlasmaMapper, VManager, inthewords) and their bump path. | XS | Sets the expectation that v1.0 is the moment consumers MUST upgrade past 0.x. |
| **N-3** | mgf-common | **Stash cleanup.** `git stash list` shows `stash@{0}: On master: PlasmaMapper-temp-stash` — unrelated to mgf-common's working set, may be stale. | XS | Hygiene; doesn't block release. |
| **N-4** | mgf-common | CHANGELOG `## [Unreleased]` currently says `(no entries)` — accurate post-0.34.0. (No action needed.) | — | — |
| **N-5** | Federation-wide | **Cross-platform CI matrix (RA-27 in mgf-common, deferred to v1.1+).** If v1.0 ships Linux-only, README should explicitly say "platform: Linux first; Windows / macOS at v1.1+". | XS docs | Currently implicit. |
| **N-6** | mgf-common | **Strategic review + federation roadmap docs** are still in `docs/inprogress/`. Either archive them at v1.0 (per DOC-11 archival pattern) or promote to a permanent home. | XS | Same archival pattern used for STANDARDS_AUDIT trackers worked well — apply here. |
| **N-7** | Federation-wide | **Confirm each sibling's README accurately describes the v1.0 surface.** Some READMEs reference v0.x extraction history (good context) but should NOT promise unshipped features. | XS each | Doc accuracy = first impression. |
| **N-8** | mgf-common | **PAPER-10 deferred — entry-point-driven plugin auto-discovery.** Confirm explicit v1.1+ defer in FEEDBACK.md (currently shows "DEFERRED"). | XS | Status update only. |
| **N-9** | mgf-common | **`V2_6_NOTIFICATION.md` + `V2_7_NOTIFICATION.md`** were broadcast notes. Once all siblings have absorbed them (✓ done this session for v2.6 shape), they can be archived under `docs/archive/2026/`. | XS | Hygiene. |
| **N-10** | All siblings | **`mgf-common>=0.4,<0.5`-style pin convention** — [`federation_roadmap.md`](federation_roadmap.md) D7 says siblings pin "ONE minor at a time." At v1.0, the pin shape changes to `>=1.0,<2.0`. Document the cut. | XS | Lock the convention for v1.0+. |

---

## §4 Already-done this session — confirmed v1.0-ready

For reference / completeness. Items in this section are NOT
blockers — they're called out so the next session knows what
"the heavy lifts" already produced.

| Project | What landed | Commit |
|---|---|---|
| mgf-common | **0.34.0 published** (PyPI + Codeberg tag `v0.34.0`); P0+P1 all closed; P2 8/10 closed; standards v2.6 + v2.7 shipped. | `e2b02da` |
| mgf-alembic | All P0+P1 closed; **P2 fully closed**; v2.6 Shape declaration. 33 tests pass. | `3cc94ed`, `c677074` |
| mgf-apiprobe | All P0+P1 closed; SH-06 + PC-06/08 closed; RA-04 flipped; v2.6 Shape declaration. **791 + 17 perf pass; 1 pre-existing version-drift failure (B-2).** | `b57d2b4`, `de79db7` |
| mgf-django | All P0+P1 closed; **RA + PC trackers 100% closed**; SH 4/5 (SBOM deferred). v2.6 Shape declaration. 39 + 6 perf pass. | `80a2ef4`, `1984f26` |
| mgf-fastapi | All P0+P1 closed; RA-06 + RA-08/SH-05 + PC-05/06 closed; v2.6 Shape. 137 + 6 perf pass. | `cc6a713`, `98f1f15` |
| mgf-http | All P0+P1 closed; **RA tracker 100% closed**; SH 4/6 + PC 5/6. v2.6 Shape. 71 + 9 perf pass. | `20f317e`, `82bb381` |
| mgf-sqlalchemy | All P0+P1 closed; RA-03..05 + PC-03/04 closed via code; v2.6 Shape. 78 + 5 perf pass. | `27c2d8f`, `73515ca` |
| mgf-website-template | **All 21 P2 items closed (RA/SH/PC trackers = 0 open)**; v2.6 Shape (template variant); dependabot config + deploy.yml hardening. | `d80e0c1`, `4251a2e` |

---

## §5 Explicitly deferred to v1.1+ (decision-of-record)

These are the tracker entries flipped from `⏳ open (P2)` to
`⏳ v1.1+ — <rationale>` during the P2 sweep. They're NOT
v1.0 blockers; documented here to prevent re-litigation.

| Theme | Items | Rationale |
|---|---|---|
| **Federation SBOM workflow** | mgf-alembic SH-05, mgf-apiprobe SH-07, mgf-django SH-04, mgf-fastapi SH-07, mgf-http SH-06, mgf-sqlalchemy SH-05 | One cross-sibling effort; pulled into v1.0 via item **I-1** if scoped. |
| **CI infrastructure** | mgf-common RA-27 (cross-platform CI), mgf-common PC-06 (CI baseline diff) | Linux-first v1.0 with documented platform support. |
| **Mutation / fuzz testing** | mgf-common RA-24 (mutmut / cosmic-ray), RA-25 (codemod fuzz corpus) | 1413 unit tests provide v1.0 confidence; mutation/fuzz is post-v1.0 hardening. |
| **Needs real-consumer feedback** | mgf-apiprobe RA-05/06/07, mgf-fastapi RA-05/07, mgf-sqlalchemy SH-04 (RLS helper shape) | Right design needs production failure data before the public surface locks. |
| **Cosmetic / no-clear-trigger** | mgf-common RA-23 (vault secret-clearing — Python-string immutability makes it largely cosmetic), mgf-common PC-08 (1k-file codemod timing — no clear regression trigger) | Documented + accepted. |
| **Diagnostic / docs polish** | mgf-apiprobe SH-05 (extra security checks), mgf-fastapi SH-06 (test-app CORS docs), mgf-http SH-05 (cert pinning recipe) | Quality-of-life, not v1.0 correctness. |
| **Profile / no-perf-gate-shape** | mgf-apiprobe PC-05/07, mgf-fastapi PC-07, mgf-http PC-06, mgf-sqlalchemy PC-05 | Memory/allocation profiles or harness scaffolding without a clear regression trigger. |
| **Closed-elsewhere by reference** | mgf-common RA-23 (Python-string immutability accepted) | Documented + accepted limitation. |

**Total deferred:** ~19 items across the federation. Each
carries `⏳ v1.1+` in its tracker with rationale inline.

---

## §6 Memory-of-the-watch items

Saved-memory entries that touch this work — worth a glance before
acting on the list above.

- **[`reference_pypi_new_project_rate_limit`](../../../.claude/projects/-home-bassam-PycharmProjects-mgf-common/memory/reference_pypi_new_project_rate_limit.md)**
  — PyPI's "Too many new projects" 429 has a ~24h cooldown.
  Affects **B-1** (mgf-django first publish).
- **[`feedback_pypi_vs_local_dist`](../../../.claude/projects/-home-bassam-PycharmProjects-mgf-common/memory/feedback_pypi_vs_local_dist.md)**
  — PyPI is the contract; local `dist/` is not. Confirmed
  during 0.34.0 publish; preserved.
- **[`project_codeberg_naming`](../../../.claude/projects/-home-bassam-PycharmProjects-mgf-common/memory/project_codeberg_naming.md)**
  — newer sibling slugs use hyphens (mgf-fastapi);
  legacy repos keep underscores. Confirmed across all 8 pushes.
- **[`feedback_stay_in_0x_for_now`](../../../.claude/projects/-home-bassam-PycharmProjects-mgf-common/memory/feedback_stay_in_0x_for_now.md)**
  — until the maintainer explicitly green-lights v1.0, the
  working stance is climb 0.34/0.35/... This document was
  written in response to v1.0 readiness questions; if the
  green-light is not yet given, items **B-6 + B-7** stay
  pending and the rest of the list applies to whichever 0.x
  release is next.
- **[`feedback_split_by_runtime_shape`](../../../.claude/projects/-home-bassam-PycharmProjects-mgf-common/memory/feedback_split_by_runtime_shape.md)**
  — each sibling owns the framework deps it needs at runtime.
  Confirmed clean across the federation.

---

## §7 Source-of-truth pointers

When this document goes stale (it will — release work moves
fast), prefer these canonical sources:

- **Per-sibling tracker state**: each sibling's
  `docs/inprogress/{RELIABILITY_AND_ACCURACY,SECURITY_HARDENING,
  PERFORMANCE_AND_CAPACITY}.md` — counts at top, work units
  in `§4`, closed items in `§5`.
- **mgf-common's published surface**:
  [`PUBLIC_API.md`](../../PUBLIC_API.md) +
  [`PUBLIC_API.json`](../../PUBLIC_API.json) — both regenerated
  before every PyPI publish.
- **The federation split plan**:
  [`federation_roadmap.md`](federation_roadmap.md) — the
  authoritative v0.28 → v1.0 narrative + quality gates.
- **Strategic review**:
  [`strategic_review.md`](strategic_review.md) — the
  "why are we doing this" backlog.
- **Standards compliance**:
  [`STANDARDS_COMPLIANCE.md`](../STANDARDS_COMPLIANCE.md) — the
  rolling audit of mgf-common against its own standards.
- **Release engineering**:
  [`docs/standards/RELEASING.md`](../standards/RELEASING.md) —
  REL-01..REL-09 rules.
- **PyPI ground truth**: `pip index versions <name>` —
  what's actually shipped.

---

## §8 How to use this document

- **At the start of a v1.0-release session**: read §3 + §6.
  §3 is the action list; §6 is the constraint set.
- **When closing an item**: flip it from open to closed in the
  appropriate tracker first; then update this doc's §3 row to
  reflect the new state (or remove the row entirely).
- **When deferring**: add the rationale to §5 + the sibling's
  own tracker entry.
- **When this doc is fully cleared**: archive under
  `docs/archive/2026/` per the **DOC-11** pattern, just like the
  STANDARDS_AUDIT trackers.

---

## §9 Retrospective log

| Date | Event |
|---|---|
| 2026-05-17 | Document created at the close of the federation P2 sweep + mgf-common 0.34.0 publish. Captures the state at HEAD of every project. |
| 2026-05-17 (later) | **Phase 1 complete:** B-2 (mgf-apiprobe version drift fixed, commit `b819503`) + N-3 (stale `PlasmaMapper-temp-stash` dropped — it was the abandoned v0.27→v0.28 fastapi-extraction WIP from months ago). |
| 2026-05-17 (later) | **Phase 2+4 started — token-available siblings shipped:** mgf-http 0.3.0 → PyPI ✓ (commit `91ec738`, tag `v0.3.0`); mgf-apiprobe 0.4.0 → PyPI ✓ (commit `f39c1d6`, tag `v0.4.0`). Both pushed to Codeberg + verified on PyPI. mgf-alembic 0.3.0 committed `f3d3473`, tagged `v0.3.0`, pushed to Codeberg, **wheel + sdist built locally in `dist/` — PyPI upload pending PyPI token** (see §10). |
| 2026-05-17 (later) | **Blocked-on-credentials:** mgf-sqlalchemy 0.3.0 (not started), mgf-fastapi 0.5.0 (blocked on mgf-sqlalchemy), mgf-django 0.3.0 + first-PyPI-publish — all waiting on token slots in `my_stuff/keys/pypi_tokens.txt` lines 17, 20, 23. |
| 2026-05-18 | **Wave 1–3 work superseded most of this doc's blockers.** Phase A (federation FEEDBACK + MGF_ADOPTION + MGF_STANDARDS_CONFORMANCE) closed B-3 (sibling pins refreshed) and unblocked I-3 (cross-repo discipline rules — DOC-13/14/15 + FB-01..04 + ADP-01..04 + CFM-01..04 now in `docs/standards/`). Wave 1 (mgf-common 0.35.0 + 0.36.0) shipped 22 new public names. Wave 2 (six sibling minors) shipped 26 names. Wave 3 (mgf-common 0.37.0) shipped 17 names. Wave 4 audit produced `v1_0_stability_audit.md` (B-5 / I-2 superseded — promotion proposal now formally documented) and `v1_0_quality_gates.md` (consolidates all remaining v1.0 blockers into Q-1..Q-5). |
| 2026-05-18 | **Remaining v1.0 blockers (re-keyed from Wave 4 audit):** **Q-2** N818 exception-name rename decision (a) vs (b); **Q-4** PyPI backlog drain (all 7 federation packages drifted from PyPI — same blocker as B-1, now affects every sibling not just mgf-django); **Q-11** maintainer signs off §3 promotion proposals in `v1_0_stability_audit.md`. Everything else (Q-1 sibling PUBLIC_API.json port, Q-3 ruff auto-fix sweep, Q-5 standards-sync script) is carry-forward to v1.0.1 / v1.1. |

---

## §10 PyPI token state (as of 2026-05-17 session end)

| Sibling | Token slot | Status | Action |
|---|---|---|---|
| mgf-common | line 3 | ✓ populated | (used for 0.34.0 ship) |
| mgf-apiprobe | line 7 | ✓ populated | (used for 0.4.0 ship) |
| mgf-fastapi | line 10 | ✓ populated | (pending mgf-sqlalchemy release) |
| mgf-http | line 13 | ✓ populated | (used for 0.3.0 ship) |
| **mgf-sqlalchemy** | **line 17** | **✗ EMPTY** | **needed** |
| **mgf-alembic** | **line 20** | **✗ EMPTY** | **needed (build + tag already done — only twine upload pending)** |
| **mgf-django** | **line 23** | **✗ EMPTY** | **needed + first-publish workflow (rate-limit risk)** |
| mgf-website-template | line 26 | N/A | not a PyPI package |

**To unblock:**

1. Sign in at <https://pypi.org/manage/account/token/>.
2. Create per-project scoped tokens (recommended — same shape as
   the existing ones). Scope: one token per project (mgf-sqlalchemy,
   mgf-alembic, mgf-django). For mgf-django: scope to "Entire account
   (all projects)" for the FIRST publish since the project doesn't
   exist yet on PyPI — then rotate to project-scoped after the
   project is registered.
3. Paste each token into `my_stuff/keys/pypi_tokens.txt` on the
   appropriate "(empty)" line (17 / 20 / 23 — the line directly
   AFTER the `Project mgf-<name> scope:` header).
4. Once populated, the next session can run `uv tool run twine
   upload dist/*` from each sibling and complete the release —
   no other work needed for mgf-alembic; mgf-sqlalchemy and
   mgf-django still need their full release flow (commits + tags
   + CHANGELOG + build + upload).

**mgf-django first-publish notes:**

- PyPI returns no project at <https://pypi.org/project/mgf-django/>
  today — the package name has never been registered.
- First publish creates the project and ATTACHES the uploading
  account as owner. Use a token scoped "Entire account" for that
  upload, then rotate to project-scoped per **REL-04**.
- Rate-limit: PyPI throws 429 "Too many new projects created" with
  a ~24h cooldown if too many new-project creates happen in a
  window (per the
  [`reference_pypi_new_project_rate_limit`](../../../.claude/projects/-home-bassam-PycharmProjects-mgf-common/memory/reference_pypi_new_project_rate_limit.md)
  memory). If the upload 429s, retry the next day.
