# Strategic review — making `mgf-common` better

> **Status:** working document. Captures a senior-TC-level review
> conducted 2026-05-01, with the maintainer's trajectory answers
> baked in. Not a finished plan — a backlog to work from.
> **Audience:** the maintainer + future Claude sessions picking
> up strategic work.

---

## Context

### What `mgf-common` is

A typed Python infrastructure library — typed exceptions,
structured logging, OpenTelemetry tracing, layered settings,
credential vault, lifecycle bootstrap, CLI scaffolding — plus
the engineering-standards docs every consumer is held to. The
federation leaf under the `mgf.*` namespace.

### Where it stands today (2026-05-01)

- 10 commits in the fresh-history master branch.
- 162 public names across 18 modules.
- 1213 tests passing (ruff / mypy --strict / pytest /
  lint-imports all green).
- Recent ships: `VersionedFileStore[T]` (PAPER-04),
  `register_vault_backend(config_model=)` (VAULT-01),
  observability shim recipe (PAPER-03), multi-tenancy position
  paper (ASPIRE-01), `doctor --standards` (NICE #12),
  cookiecutter template (NICE #15), tutorial doc (NICE #17),
  federation doc (NICE #18).
- One reference consumer: VManager.
- One sibling: `mgf-apiprobe`.
- Open FEEDBACK §2 entries: PAPER-06 (deferred),
  PAPER-07 (path helpers), PAPER-08 (catch-up should detect
  closed-box leaks).

### Trajectory (from the maintainer, 2026-05-01)

- **Org library first.** Primary purpose: serve the
  maintainer's organization's projects.
- **Industry-usable.** Available on PyPI; no NDA / no walled
  garden. External orgs MAY adopt if they choose; the project
  doesn't go out of its way to recruit, but doesn't reject
  either.
- **Current friction:** none surfaced. Next planned work is
  consumer-side (VManager catch-up to current `mgf-common`
  major).
- **Stability posture:** "stay in 0.x for now" — see saved
  feedback. v1.0 deferred until explicit green-light.

The trajectory matters: every priority below assumes "org-first,
public-second." If that flips (becomes a primary OSS push), the
weights shift toward **discoverability + community process**
over the org-internal items.

---

## Priority 1 — Get a second, independent consumer

> **✅ ACHIEVED — 2026-05-05.** `inthewords` (Django web app)
> landed as the second independent consumer. `PlasmaMapper`
> (FastAPI service, small) followed as the third. Both filed
> substantive friction during their adoption walks, validating
> the second-consumer thesis: a single afternoon of independent
> consumer evidence surfaced six 🟡-High architectural papers
> (HTTP-01, CONFIG-01, LOG-01, LOG-02 + PAPER-13/15-partial/16/17)
> that VManager's co-developed shape couldn't have surfaced.
> Closure recorded in [`FEEDBACK.md`](../../FEEDBACK.md) ASPIRE-02.
> The "second consumer evidence" gate now also unblocks
> [PAPER-06](../../FEEDBACK.md#paper-06--docsstandards-cites-the-reference-consumer-with-fileline)
> (reference-consumer restructure) — tracked as a follow-up
> for a future minor.


### The advice

Recruit (or commission) a second consumer that uses `mgf-common`
in earnest, built by someone who didn't co-author the library.

For an **org library** trajectory, this means:
- An *internal* tool — yours or a teammate's — that doesn't
  already use `mgf-common`. Port it.
- A peer build session: another developer takes the tutorial,
  builds a small thing, you take notes silently.
- A new internal project starting from the cookiecutter
  template — observe friction.

Apiprobe doesn't count for this purpose. Apiprobe validates the
**federation pattern**, not the **API**. It uses a tiny slice
of `mgf-common`'s surface and was co-authored alongside.

### Why this is #1

Every API decision in the library is shaped by VManager's
needs, even when no one notices. The reference consumer
co-evolved with the library, so it cannot independently audit
what's awkward, missing, or overbuilt. Until a second consumer
hits "wait, why does this work this way?", you're optimizing
for one shape with N=1 evidence.

The signal value is enormous: every concern raised by an
outside consumer in a single afternoon is worth more than a
quarter of self-audit. The whole `FEEDBACK.md` machinery is
designed for this — but it has nothing to triage against
because there's only one voice.

This also unblocks **PAPER-06** (the reference-consumer
restructure) which was deferred specifically waiting for a
second consumer.

It is also the gating evidence for
[**PAPER-10**](../../FEEDBACK.md#paper-10--no-entry-point-driven-auto-discovery-for-observability--vault-plug-ins)
(entry-point-driven plugin auto-discovery). The two are the
same problem viewed from different angles: P1 asks "who else
adopts the library, and how?", PAPER-10 asks "what shape does
that adoption demand of the extension surface?". When P1's
signal arrives, PAPER-10's deferral lifts automatically; no
separate watch is needed.

### Concrete next steps

- [ ] Identify 1-2 existing internal tools that aren't on
      `mgf-common` yet. Pick the one with the most
      observability / config / error-handling surface. Port it.
- [ ] Schedule one 2-hour "stranger walks the tutorial" session
      with a teammate. Take notes silently — don't help. File
      every confusion / friction as a `FEEDBACK.md` entry.
- [ ] After the port + session: review the new FEEDBACK
      entries. Re-prioritize the open queue.

### Success criterion

The next review (3-6 months out) lists ≥2 production consumers
in the "Reference consumers" section. PAPER-06's full
restructure has shipped because a second consumer exists.

---

## Priority 2 — Operational maturity

### The advice

Invest in **automated** infrastructure that catches drift
before consumers do.

The library is on PyPI, has a `.woodpecker.yml`, has manual
`twine upload` — but nothing fires unattended. When Python
3.13 lands a backwards-incompatible deprecation, when pydantic
ships v3, when a transitive dep cuts a CVE, you'll learn from
a consumer's traceback rather than a green check that turned
red overnight.

### Why this matters at the org-library trajectory

Org consumers depend on the library for production. They
expect early warning on upstream rot. If they have to file the
bug report that surfaces "pydantic 3 broke us," the library's
positioning (cornerstone, industry-standard) erodes faster than
any one bug.

For an org-first trajectory specifically: internal consumers
won't have the "I'll just file an issue" reflex of OSS
consumers. They'll patch around silently, or migrate off. By
the time you find out, the trust damage is done.

### Concrete next steps, in order

1. **CI on every push** (highest priority of this priority).
   Either:
   - Woodpecker via `ci.codeberg.org` access (the
     `.woodpecker.yml` is correct; just needs the runner
     enabled at the project level — your action in the
     codeberg web UI).
   - Or: GitHub mirror + GitHub Actions running the same
     four-gate matrix. The repo could be mirrored read-only.

2. **Nightly job: latest unpinned deps.** A separate matrix
   that resolves pydantic / opentelemetry / httpx / fastapi /
   sqlalchemy to their latest releases (not the floor pin) and
   runs the full test suite. Failures are early-warning of
   upstream rot. Recommend: 2 jobs/week (Mon + Thu) so the
   maintainer sees breakage before downstream consumers do.

3. **Automated PyPI publish on tag push.** Today's manual
   `twine upload` is fragile (forgotten env var, wrong key,
   version mismatch, missed gates). When codeberg-→-PyPI
   trusted-publisher OIDC lands, switch immediately. In the
   meantime, a developer-laptop release script with safeties
   (matches pyproject? tag matches `__version__`? all four
   gates green? wheel + sdist build clean? `twine check`
   passes?) is a solid stopgap.

4. **Dependabot or equivalent.** Surface security advisories
   on transitive deps. `pip-audit` is in the woodpecker config
   but only fires when the runner does. Until #1 lands, run
   it weekly via cron locally.

5. **Performance regression gate.** The `bench` subcommand
   exists; tie it into CI. A >25% regression on any row fails
   the build. Numbers update when a benchmarked operation
   changes intentionally.

### Success criterion

- Every push surfaces a green/red signal within 5 minutes.
- A new pydantic / opentelemetry / httpx release breaks the
  nightly job within 24h, not on a consumer's deploy.
- A new tag triggers PyPI publish without a developer machine
  in the loop.
- A performance regression on a load-bearing primitive fails
  the PR that introduced it.

---

## Priority 3 — Document the v1.0 question (or the explicit "we don't")

### The advice

I respect the saved-feedback position ("stay in 0.x; v1.0
deferred"). As a CTO I'd push you to *make the position
explicit and document the criteria*. Right now it's implicit;
make it observable to outside readers.

### Why this matters at the org-library trajectory

Even for org-internal use, every public symbol being
`experimental` means **no one inside or outside can plan a
major investment around `mgf-common`**. The `experimental`
contract reads as "this might rewrite next minor." That signal
is honest if true; if it's actually "we're stable but haven't
declared it," the signal is misleading and discourages
investment.

For the optional industry-side adoption: an external evaluator
who reads `experimental` on every name and walks away isn't
wrong. They're reading what was written. Document the position
or accept that you've opted out of that population.

### The decision space

- **Option A — "We ship 1.0 when X."** X could be: ≥2 consumers
  in production for ≥6 months without a breaking change;
  standards docs frozen; the closed-box rule passes a hostile
  audit. Whatever the criteria, write them down. The criteria
  themselves don't have to be ambitious — even "1.0 ships when
  the maintainer says so" is honest.

- **Option B — "We stay 0.x forever; stability tiers per
  symbol carry the contract."** Then: which symbols are
  `stable` today? When does the first promotion happen?
  What's the promotion criterion?

- **Option C — Don't decide. The status quo.** Honest about
  uncertainty, but the cost is the consumer-investment signal
  above.

### Concrete next steps

- [ ] Write a short `docs/public/stability.md` (or a §
      addition to `docs/standards/API_DESIGN.md`) that picks A
      or B and commits to criteria. Two pages, no more.
- [ ] If Option A: list the criteria. Track them on a closed
      list — if all check off, the next minor is the
      pre-1.0 RC.
- [ ] If Option B: pick 2-3 symbols that meet the (currently
      undefined) `stable` bar today. Promote them in the next
      release. Document the criterion you used.
- [ ] Update `PUBLIC_API.md`'s "Notes on the experimental
      tier" section to reflect the decision.

### Success criterion

A consumer evaluating the library can read one document and
know, within 5 minutes: "is this safe to invest in for our 5-
year project?"

---

## Honorable mentions (priority 4-6)

These are real and worth shipping, but rank below the top three
at this stage.

### #4 — Performance contract

**The gap:** the `bench` subcommand exists but the numbers
aren't a public promise. The library claims "single-call
bootstrap" / "30 ns contextvar lookup" but doesn't formally
commit to those numbers.

**The fix:** turn the bench output into a contract. Document
the numbers in `docs/internal/BENCHMARKS.md` (already exists)
as **promises, not snapshots**. CI fails on regression beyond
the documented threshold. When numbers improve, update the
doc; when they regress beyond budget, the PR fails.

**Why it ranks #4:** it's a real consumer-trust signal but only
matters once consumers hit the perf-sensitive paths. Most
consumers don't.

### #5 — Error UX audit

**The gap:** the deprecation warnings shipped recently are
good. The PEP 678 notes on `AppConfigError` are good. But the
broader error surface is unaudited. Some "your config is
wrong" paths have actionable hints; others don't. Some
exceptions carry machine-readable fields (per EH-08); others
don't.

**The fix:** systematic walk through every public exception
class and every `raise` in the public surface. Score each on:
- Does it carry machine-readable fields?
- Does it ship PEP 678 hints when raised in foreseeable
  consumer-side mistakes?
- Is the message specific enough to guide a fix without
  reading the source?

Fix the worst-scoring 20%.

**Why it ranks #5:** real value but lower urgency than
operational + adoption-signal items.

### #6 — Discoverability

**The gap:** for a library positioning as a "cornerstone,"
the discovery surface is thin: codeberg + PyPI. No docs site
(readthedocs / sphinx hosted). No GitHub mirror (cuts off the
SEO + contributor surface that GitHub provides). No
announcement / blog post.

**The fix at the org-first trajectory:** modest. A docs site
served from the wheel-bundled `docs/standards/` (the
`mgf-common standards <topic>` CLI is the offline equivalent;
a web mirror would help external readers). A read-only GitHub
mirror that consumers hit via `pip install mgf-common`
references in their pyprojects.

**Why it ranks #6:** at the org-first trajectory, discovery
matters but is a force-multiplier on adoption, not a
prerequisite. Get a second consumer first; THEN make it easier
for the third + fourth to find you.

---

## Things I considered but rejected (with reasoning)

- **"Pre-1.0 plug-in discovery for `mgf-common`."** Apiprobe
  has entry-points-driven plug-in groups; `mgf-common` doesn't
  yet. Speculative — no consumer is asking for it. Defer until
  a real consumer requests an extensible registry.

- **"Async-first migration."** Most modern Python infrastructure
  is going async-first (httpx, sqlalchemy 2.x, fastapi). The
  library is dual-mode (sync-first with asyncio sibling
  primitives). Could argue for an async-first surface. But the
  rewrite cost is enormous and there's no consumer pain — VManager
  is a Qt desktop tool that's sync-first. Defer.

- **"Beyond Python."** Org consumers might use JS / Go / Rust
  components alongside the Python ones. `mgf-common` is
  Python-only. Out of scope; the maintainer's pattern is
  Python-first for the org.

- **"Commercialize."** Not part of the trajectory.

---

## Maintainer's questions

When picking up this doc later, the questions worth re-asking:

1. **What's blocking second-consumer recruitment?** Is it
   maintainer time? A teammate's availability? The risk that
   the second consumer's friction is going to surface lots of
   ugly work?

2. **Is the current "no friction" answer durable, or just
   "no friction visible right now"?** If you've been silently
   working around something, that's signal worth higher
   priority than anything I listed.

3. **What does "graduated org library" look like 2 years out?**
   3 consumers? 10? 20?  This answer tightens or relaxes the
   v1.0 timeline considerably.

---

## Update log

| Date | Author | Change |
|---|---|---|
| 2026-05-01 | Bassam (decision) + Claude (drafted) | Initial review captured. Trajectory answered (org-first, public-optional). Three priorities + three honorable mentions identified. |
