# Scope

> **Conformance:** L1 / L2 / L3 (see `README.md`)
> **Audience:** Library authors deciding what to ship; reviewers
> deciding whether a PR belongs in this codebase; consumers
> looking for "what should I expect this library to handle, and
> what do I need to bring myself".
> **Propagation (DOC-18):** changing a rule here ripples — walk DOCUMENTATION.md §10.4 and update its dependents in the same commit (README *Index of MUSTs*, the `mgf-fed standards` / `agents` CLI, and any parallel copies / pins the matrix names).

> **Scope.** Cross-project scope policy for any shared
> infrastructure library — what it ships, what it deliberately
> does NOT ship, and the reasoning behind each cut. Written for
> the `mgf-common` shape but applies to every "cornerstone"
> library that aspires to be a stable foundation for downstream
> consumers without becoming a kitchen-sink. Lifted from
> `docs/CLOSED_BOX_REDESIGN.md` §8 as the canonical scope
> statement.

---

## Rules box (read this first)

| ID | Rule |
|---|---|
| <a id="SP-01"></a>**SP-01** | **The library MUST publish an explicit IN-scope list.** Without it, "scope creep" arguments collapse into taste arguments. The list is the bar new modules clear. |
| <a id="SP-02"></a>**SP-02** | **The library MUST publish an explicit OUT-of-scope list with reasoning.** "Why we don't ship X" + "what you should use instead" are non-negotiable per entry. The reasoning answers the inevitable "but couldn't you also..." pitches. |
| <a id="SP-03"></a>**SP-03** | **Every PR adding a new module or top-level function MUST justify against §1 (IN scope) or §3 (Adjacent).** A PR whose answer is "OUT" is rejected with a pointer to the existing ecosystem solution. |
| <a id="SP-04"></a>**SP-04** | **Adjacent-scope items MUST require a documented consumer demand signal** before promotion to IN. "We might want this someday" is not a signal; "consumer X is forking the library to add this" or "≥2 consumers ask in FEEDBACK.md" is. |
| <a id="SP-05"></a>**SP-05** | **The OUT-of-scope list MUST cite the ecosystem alternative.** Every cut belongs to a function someone in the ecosystem already does well — say which one. Without the citation, the cut looks lazy instead of considered. |
| <a id="SP-06"></a>**SP-06** | **Scope policy MUST be revisited on every MAJOR release.** Items move between IN / Adjacent / OUT as the ecosystem evolves and consumer demand shifts. The MAJOR boundary is the only place a previously-IN item can be removed (with a deprecation cycle per AP-04). |

---

## 1. IN scope

The library ships **infrastructure that every Python desktop /
service / mobile project needs once it crosses the
spike-to-production boundary**:

- **Identity + lifecycle** — configure once; resolve everywhere.
- **Typed configuration** with file + env + CLI layering.
- **Typed exception hierarchy** with translation seams.
- **Structured logging** + redaction + sinks.
- **OpenTelemetry tracing** / metrics integration.
- **Crash reporting** (local + remote).
- **Excepthook installation** for every entry-point shape (sys,
  threading, asyncio).
- **Vault abstraction** for secrets storage.
- **Cancellation + progress** primitives.
- **Subprocess wrapper** — `mgf.common.process.run` (timeout,
  redaction, OTel-correlated stream capture, typed
  `ProcessResult`) AND **service lifecycle orchestration** —
  `mgf.common.process.Service` (start / wait-ready / stop;
  context-manager-shaped for dev / test / script use).
  Deliberately NOT a systemd-style supervisor (no restart
  policies, no dependency graphs — that's `systemd` /
  `supervisord` / a Procfile's job; see §2 OUT-of-scope).
- **Resilience patterns as STANDARDS** — retry with
  exponential-backoff-plus-jitter (**EH-11**), idempotency
  (**EH-12**), and circuit breaker (**EH-13**) are codified
  in [`ERROR_HANDLING.md`](ERROR_HANDLING.md) as MUSTs. The
  **utility implementations** (a generic `retry()` decorator,
  a `CircuitBreaker` class, a `Bulkhead` semaphore wrapper)
  are §3 Adjacent — consumers implement the standards in their
  own code today; lift to `mgf.common.resilience` when ≥2
  consumers ship near-identical copies.
- **Cross-platform path conventions** — XDG / macOS Application
  Support / Windows AppData.
- **A diagnostic + maintenance CLI** for self-introspection and
  consumer-side automation (`mgf-fed explain / validate /
  app-doctor / bench / standards / init-project / feedback-howto /
  migrate / catch-up`). The CLI grew from three diagnostic
  subcommands to nine as consumer needs surfaced — each
  earned its slot via the "every consumer needs this" test.
- **A reliability + security + performance tracker discipline**
  — `RELIABILITY_AND_ACCURACY.md` / `SECURITY_HARDENING.md` /
  `PERFORMANCE_AND_CAPACITY.md` priority-table format
  (priority → first wave → work units → invariants → closed
  items, with verified `file:line` evidence per item). The
  three companion trackers ship as part of the library's own
  documentation; the *format* is reusable by every consumer
  for its own infrastructure debt.
- **A scope policy** — *this document*. Other libraries that
  follow this standard ship their own `SCOPE.md`.

The unifying property: every IN-scope item is something **every**
consumer needs by the time they go to production, AND has no
single ecosystem alternative the library can defer to. Logging
has the stdlib but you build a structured-logging stack on top of
it. Config has pydantic-settings but you build a layered loader on
top. Exceptions have Python's hierarchy but you build a typed,
translation-seamed hierarchy on top.

If the bullet point above could read "use library X" without loss
of generality, it belongs in §2.

## 2. OUT of scope (we will NOT ship these)

| Out-of-scope | Why we don't | What you should use instead |
|---|---|---|
| **GUI framework** | Domain-specific; PySide6 / Tk / Flet / Toga have non-overlapping ecosystems. We CAN ship `mgf.common.gui.<framework>` adapters when a consumer needs one. | Bring your own |
| **ORM / data layer** | Already has SQLAlchemy / SQLModel / Django / Tortoise / etc.; no value-add we can offer. | SQLAlchemy is the ecosystem default |
| **HTTP server framework** | FastAPI / Flask / Litestar / Starlette / aiohttp — every shape covered. | Pick one; integrate via `mgf.common.observability` for shared logging / tracing |
| **HTTP client (general-purpose)** | `httpx` is the ecosystem default; we ship `httpx` instrumentation but not our own client. | `httpx` |
| **Full async runtime** | `asyncio` / `trio` / `anyio` are the language; we provide async adapters where relevant. | stdlib `asyncio` (or trio via anyio shim) |
| **Cloud-provider deployment recipes** | AWS / GCP / Azure are non-overlapping; per-provider tooling exists. | provider's own SDK + IaC tool |
| **AI / ML utilities** | Orthogonal domain. | use the relevant ML library directly |
| **Plugin loading / discovery framework** | We use registries for OUR extensions; consumer plug-in loaders use stdlib `importlib.metadata.entry_points`. | stdlib `importlib.metadata` |
| **i18n translation engine** | We provide a hook (`_localise(msg, **vars)`) per FEEDBACK §3.13; consumer brings the catalog. | stdlib `gettext` or `babel` |
| **Job queue / task scheduler** | Celery / RQ / arq / dramatiq cover this; we provide cancellation + progress primitives that integrate with any of them. | celery / rq / arq |
| **Database migration tool** | Alembic / aerich / etc. cover this. | alembic |
| **Testing framework** | pytest is the ecosystem default; we ship pytest fixtures (FEEDBACK §3.7) but not our own runner. | pytest |
| **Build / packaging tool** | hatchling / setuptools / poetry / uv cover this. | hatchling (the library's own choice) |
| **Linter / formatter** | ruff / black / isort cover this. | ruff |

The pattern: every cut has a citation. If a future PR proposes
adding one of these, the response is the cited tool — not "no, we
just don't want to". Citations age; this list MUST be revisited
per rule **SP-06**.

## 3. Adjacent scope (we MAY ship later, requires consumer demand)

Items that fit the cornerstone shape but lack a consumer demand
signal yet (per **SP-04**, "consumer X is forking the library to
add this" or "≥2 consumers ask in FEEDBACK.md").

- **SSH client wrapper** — `paramiko` / `asyncssh` wrapped with
  the same translation-seam / typed-error / OTel-correlated
  discipline as `process.run`. Listed as IN-scope intent in
  earlier SCOPE.md versions; never landed in core. Promote to §1
  when ≥2 consumers need it — until then, consumers implement
  the per-project shape from [`COMMON_COMPONENTS.md`](COMMON_COMPONENTS.md) §6 OR keep
  paramiko's surface direct.
- **Resilience utility implementations** — a generic `retry()`
  decorator with EH-11 jitter, a `CircuitBreaker` class
  (EH-13), a `Bulkhead` semaphore wrapper. The STANDARDS exist
  (EH-11/12/13 are MUSTs); the SHARED UTILITIES are not yet
  shipped. Lift to `mgf.common.resilience` when ≥2 consumers
  ship near-identical copies.
- **OpenAPI generation from typed Settings.** Could lift the
  `BaseAppSettings` schema as an OpenAPI components schema for
  HTTP servers using our config. Wait for a consumer to ask.
- **Conda-forge / Homebrew packaging.** When ≥10% of consumer
  demand lives outside PyPI.
- **Containerised base image.** When the release cadence
  stabilises post-1.0.
- **A `qt` extra** — Qt-specific helpers (`CoreWorker`,
  `qt_excepthook`, log-viewer dialog) lifted from the reference
  consumer. Lift when a second Qt consumer arrives.
- **A `provider_pattern` module** — generic Provider ABC +
  MockProvider + FailurePlan. Lift when a second project
  would benefit from the pattern.

The pattern: each entry has a clear "promotion criterion". Until
that criterion fires, the item lives here, not in §1. The library
deliberately does NOT pre-build adjacent scope items "just in
case" — speculative additions become maintenance burden without
matching consumer payoff.

## 4. The "scope creep" review question

Every PR that adds a new module or top-level function MUST answer:

> *"Is this in §1 IN scope, in §3 Adjacent, or in §2 OUT?"*

Acceptable answers:

- **"§1 IN scope, slot N"** — the PR clears the bar. Proceed
  with normal review.
- **"§3 Adjacent, promotion criterion C now satisfied because
  ..."** — promote the item from §3 to §1 in the same PR (move
  the bullet, ship the code, update CHANGELOG). The criterion
  is what moved, not the bar.
- **"§2 OUT"** — the PR is rejected with a pointer to the cited
  alternative. The PR author is encouraged to file a FEEDBACK.md
  entry if they think the OUT decision is wrong; the standards
  doc may then change for the next MAJOR cycle (per **SP-06**).

Unacceptable answers:

- **"It's small / it's only one function / I already wrote it"**
  — scope is about boundaries, not effort.
- **"We could fold it under §1 slot N if you squint"** — slots
  are intentionally specific. If the squint is required, the
  item is §3 or §2.
- **"Other libraries do it"** — relevance to scope is independent
  of what other libraries chose; their scope policy is theirs.

## 5. Closed-box implications

"Closed-box" — one canonical, encapsulated way to do X that every
consumer imports rather than reinvents — is the property the scope
policy (SP-01..SP-06) protects, resting on the layering boundary of
**DP-01** (`DESIGN_PRINCIPLES.md`). Scope and the closed box interact:

- **Wider scope** means more closed-box surface to maintain. Every
  IN-scope item is one more import-path consumers depend on. Every
  Adjacent-scope item is a future surface bump.
- **Narrower scope** means consumers reach past the library
  boundary more often. Every OUT-of-scope item is a deferral that
  preserves the closed box at the cost of consumer integration
  work.

The scope policy resolves the tension: OUT items are deferred
because the *ecosystem* solution is good enough that the
closed-box cost would not pay for itself. IN items are accepted
because the closed-box value (one canonical way to do X across
every consumer) outweighs the surface cost.

The diagnostic + maintenance CLI is the canonical example of an
item that EARNED its IN-scope slot. It started in v0.1 as three
diagnostic subcommands (`explain` / `validate` / `doctor`) — the
"show me the config / validate this YAML / check the install
state" trio. Over the 0.x cycle it grew to nine — `bench` for
hot-path benchmarks, `standards` for inline standards-doc access,
`init-project` for new-project scaffolding, `feedback` for the
FEEDBACK.md flow, `migrate` for mechanical version-bump renames,
`catch-up` for auditing existing projects against current
standards. Each addition cleared the same bar: **without it,
every consumer reinvents the same tooling locally, fragmenting
behaviour across consumer codebases** — exactly the closed-box
leak the library exists to prevent.

## 6. Versioning of this document

| Version | Change |
|---------|--------|
| v1.0 | Introduced in standards v2.1. Codifies §8 of `docs/CLOSED_BOX_REDESIGN.md` as a freestanding policy. |

Future entries land here as scope items move between §1 / §2 /
§3 (per **SP-06**, every MAJOR release boundary).

---

*See [`README.md`](README.md) for the master standards index and
the conformance keyword definitions. See
[`API_DESIGN.md`](API_DESIGN.md) for the deprecation cycle that
governs how OUT/Adjacent/IN transitions are surfaced to
consumers.*
