# Item 2 — Ownership boundary: explicit + verified (not implied)

> Make every library's ownership boundary **explicit and verified**, the way the
> mgf-cloud vs mgf-fed case was settled — *"a fleet-wide grep settled it, I didn't
> take it on faith."* Durable tracker; machine data in `item2-ownership.json`,
> regenerated by `scripts/fleet_ownership_audit.py` (read-only).

## The gap (why this item exists)

Boundaries **are stated** — every `SCOPE.md` has SP-01..06 (purpose / in-scope /
out-of-scope-delegated / non-goals / consumers), and mgf-cloud's even names
`openstacksdk` in an out-of-scope table. But:

1. **No cross-repo verification.** `.importlinter` only guards imports *within*
   one repo (and per its own note can't even forbid `mgf.common` — a PEP-420
   limitation). **Nothing checks who owns what *across* repos.**
2. **Stated ≠ true.** Without a check, a `SCOPE.md` claim can silently drift from
   the code (see Finding **F-B** — a claim contradicted by the imports).
3. **10 repos have no `SCOPE.md` at all** (boundary not even stated).

## Methodology — "grep settled it"

`scripts/fleet_ownership_audit.py` builds two signals across all repos' `src/`:

- **Import-ownership matrix (rigorous).** AST-parse every `src/**/*.py`; classify
  each import stdlib / `mgf.*` sibling / third-party. A **non-owner that
  *imports* an owned third-party module is a real encroachment.** This is the
  hard signal.
- **String-reference triage (soft).** Greps for credentials / domain markers
  (`clouds.yaml`, `OS_AUTH`, `woodpecker`, `vault`, …). Non-owner hits are
  flagged for **triage, not auto-failed** — they're usually benign (prose,
  CLI help, or a reference to the *wrapper*).

### Worked proof — the reference case (re-verified)
- **`mgf-cloud` is the SOLE `openstacksdk` importer** — the only repo with
  `import openstack` in `src/` (3 imports; 0 in every other repo). ✅
- **`mgf-fed`'s 3 "openstack" string-hits are all benign**: CLI arg help
  (`--clouds-yaml`, `--cloud=openstack`), a comment, and a pyproject comment
  ("adapter (openstacksdk). Optional + lazily imported"). It **never imports the
  SDK, never reads a credential** — it passes args *to* the wrapper. This is
  exactly *"mgf-fed reaches VMs via the `[cloud]` extra, never holding a cloud
  credential."* → **validates import-vs-grep.**

## Verified ownership (import-primary)

| Resource | Owner | Verdict | Evidence |
|---|---|---|---|
| OpenStack compute | `mgf-cloud` | ✅ SOLE-IMPORTER | 3 imports; 0 elsewhere (fed/cloud-provision refs = benign) |
| libvirt/KVM/QEMU | `mgf-vm-libvirt` | ✅ SOLE-IMPORTER | 4 imports; vm-core/common refs = domain strings |
| pcap/dpkt forensics | `mgf-vm-forensics` | ✅ SOLE-IMPORTER | 6 imports; 0 elsewhere |
| Alembic | `mgf-alembic` | ✅ SOLE-IMPORTER | 5 imports |
| Django | `mgf-django` | ✅ SOLE-IMPORTER | 7 imports |
| Firecracker (binary) | `mgf-vm-firecracker` | · domain-owned | grep-only; vm-core refs = backend enum/fakes |
| OVA/OVF | `mgf-vm-interchange` | · domain-owned | 76 refs; owner-dominant |
| gRPC transport | `mgf-vm-sdk` | · domain-owned | 71 refs (generated stubs) |
| Redis/SSE | `mgf-livepush` | · domain-owned | sole |
| Codeberg/Woodpecker | `mgf-fed` | · domain-owned | 202 refs; fleet-wide small hits = the codeberg index URL in pyproject (noise) |
| Vault | `mgf-common` | · domain-owned | 343 refs; others use `mgf.common.vault` (correct) |
| Teltonika codec | `mgf-tele-teltonika` | · domain-owned | 94 refs; tele-core refs = seam example |

## Findings (boundary questions) — classified

Legend: 🔴 real drift/violation · 🟠 duplication to resolve · 🟢 legit (document) · ⚪ accept

| ID | Finding | Class | Evidence | Resolution |
|---|---|---|---|---|
| **F-B** | `mgf-cloud` web GUI imports `fastapi`+`uvicorn` **directly**, but pyproject claims *"FastAPI **via the mgf-fastapi**."* | 🔴 drift | `src/mgf/cloud/web/_app.py:14-16,245` vs pyproject comment | Route the GUI through `mgf-fastapi`'s app factory/middleware **or** correct the claim. Stated boundary must match code. |
| **F-SVIX** | Svix-shape webhook verification exists in **both** `mgf-auth` (`WebhookVerifier` seam) **and** `mgf-fastapi` (`webhooks/_verifier.py`) — **parallel, not layered** (fastapi doesn't depend on auth). | 🟠 dup | `mgf-auth/.../_protocols.py`; `mgf-fastapi/.../webhooks/_verifier.py:1` | Decide the owner: fastapi's HTTP-layer verifier should **delegate** to mgf-auth's seam, or document them as intentionally distinct (and why). |
| **F-A1** | `mgf-apiprobe` imports `httpx` directly (`transport/httpx_transport.py`). | 🟢 legit | core to an HTTP-**probing** tool; also uses mgf-http | Document in SCOPE: apiprobe owns a raw transport *because* it tests HTTP. |
| **F-A2** | `mgf-logship` lazy-imports `httpx` (+ fastapi/uvicorn). | 🟢 legit | the optional `[collector]` extra (a gateway to OpenObserve), not the client path | Document: the collector is an optional server-side component. |
| **F-C** | `mgf-livepush` imports `fastapi`. | 🟢 model | explicit `[fastapi]` extra, `TYPE_CHECKING`+lazy, "Optional FastAPI adapter" | None — this is the **correct** optional-adapter pattern; use as the reference. |
| **F-E** | `mgf-fastapi` imports `sqlalchemy.ext.asyncio.AsyncSession` (1, function-local type). | 🟢 minor | `src/mgf/fastapi/db.py:54,77`; fastapi depends on mgf-sqlalchemy | Optional tidy: import the type via mgf-sqlalchemy's re-export. |
| **alembic/sqlalchemy** | `mgf-alembic` imports `sqlalchemy` (7). | 🟢 legit | alembic is *built on* sqlalchemy; depends on mgf-sqlalchemy | None — same DB stack. |
| **F-XML** | `defusedxml` in both `mgf-vm-interchange` + `mgf-vm-libvirt`. | ⚪ accept | both parse XML (OVF / libvirt domain XML); no XML-wrapper lib exists | Accept; note in both SCOPEs. |

**Takeaway:** the federation mostly does boundaries *right* (optional `[extra]`
adapters — F-C is the model). The value of Item 2 is (a) **declaring** owned
externals machine-readably, (b) a **drift check** so claims stay true, and
(c) fixing the two real issues: **F-B** (drift) and **F-SVIX** (duplication).

## Explicitness gaps — no `SCOPE.md` (10)
`mgf-brand`, `mgf-cost-watch`, `mgf-website`, `mgf-website-template`, and the 6
zig libs (`mgf-common-identity`, `mgf-common-zig`, `mgf-hrb-cap/kabi/model/rt`).
Zig libs share the hrb domain — a short SCOPE per repo (or one shared) stating
the kernel-ABI / model / runtime / capability split.

## The verification / drift mechanism (bridges Item 3)
Turn the harness into a **CI gate** — the "sync mechanism that catches drift
between docs and code," applied to boundaries:
1. Each `SCOPE.md` declares its **owned externals** in a machine-readable block
   (e.g. a fenced `owns:` list: `openstacksdk`, `clouds.yaml`).
2. `fleet_ownership_audit.py --check` asserts: every declared owned import
   appears **only** in that repo (sole-importer) **and** no repo imports an
   external owned by another without a declared, paper-backed exception.
3. Drift (a new non-owner import, or a claim with no backing) **fails CI**.

## Per-library boundary-statement template
Each `SCOPE.md` gains a verified **Boundary** stanza:
> **Owns (verified):** sole importer of `<dep>` (`N` refs in `src/`, 0 elsewhere)
> + `<domain>`. **Does NOT (verified):** zero refs to `<forbidden>`; reaches
> `<capability>` only via `<seam / [extra]>`. *Evidence:* `fleet_ownership_audit.py`.

## Resume protocol
1. `python3 scripts/fleet_ownership_audit.py` → fresh `item2-ownership.json` + console verdicts.
2. Work findings by class: 🔴/🟠 fix (F-B, F-SVIX); 🟢 document in SCOPE.md; ⚪ note.
3. Add the machine-readable `owns:` block to each SCOPE.md; wire `--check` into CI (Item 3).
4. Add SCOPE.md to the 10 repos that lack one.
