# FEEDBACK — mgf-fastapi

> Paper-tracking file for issues, friction, and requests against
> this sibling. Consumer projects (PlasmaMapper, inthewords,
> vm-vmanager-app, iosRecovery, and any future adopter) file PAPER-NN
> entries here.
>
> **Where to file what:**
>
> - **mgf-fastapi-specific** → file HERE.
> - **Federation-wide** (cross-sibling patterns, standards
>   proposals) → file in
>   [mgf-common/FEEDBACK.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md).
>
> **Numbering convention:** paper numbers (`PAPER-NN`) are
> local to this repo. When a paper cross-references a
> federation-wide paper, the federation number appears in the
> metadata table as a cross-reference row.
>
> **Discipline:** see
> [mgf-standard/docs/standards/DOCUMENTATION.md](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/DOCUMENTATION.md)
> §FEEDBACK (rules FB-01..FB-04, DOC-13).

---

## §1 Process

1. **File freely.** Any consumer hitting friction MUST file
   a PAPER-NN under §2. Don't wait for permission; the
   maintainer triages.
2. **Status flips** through `OPEN` → `SHIPPED` / `DEFERRED` /
   `WONTFIX` / `MOVED` as the paper resolves. A `SHIPPED` row
   carries a commit-SHA reference + release-version reference.
3. **Renumbering** never happens within this repo — papers
   keep their PAPER-NN forever (per FB-01).

---

## §2 Open feedback (active)

#### <a id="PAPER-01"></a>`PAPER-01` — `run_test_app` doesn't cancel the lifespan task when `startup_timeout_seconds` fires

| Field | Value |
|---|---|
| Status | OPEN |
| Severity | 🟡 High |
| Submitter | mgf-fastapi supervisor adoption (2026-05-26, sibling-#2 rollout) |
| Submitted | 2026-05-26 |
| Decision | pending |
| Decided on | — |
| Scheduled for | — |
| Shipped in | — |

**Concern.** `mgf.fastapi.testing.run_test_app(app, startup_timeout_seconds=N)` raises `TimeoutError` correctly when the wrapped app's lifespan doesn't reach "started" within N seconds. But unwinding the `async with` then **waits for the lifespan task to complete** rather than cancelling it. If the lifespan body is `await asyncio.sleep(60)` (or any long sleep that doesn't honour CancelledError immediately), the test waits ~60s even though the user-facing timeout was 0.5s.

Surfaced by `tests/unit/test_testing.py::TestRunTestApp::test_startup_timeout_raises_timeout_error`: the test sets `startup_timeout_seconds=0.5` but takes ~60s to complete because of the lifespan's `asyncio.sleep(60)`. Under the federation test supervisor's `timeout = 30` default (TS-23), the test exceeded the per-test budget and hung. Worked around in the same commit by adding `@pytest.mark.timeout(120)` to the specific test; the underlying behaviour needs a real fix.

**Why it matters.** Consumers using `run_test_app` for their own integration tests get longer-than-advertised cleanup when an app's lifespan hangs. The `startup_timeout` knob promises bounded wait; the implementation breaks the promise on the cleanup side. Worse: under any supervisor or CI runner with a per-test timeout, the test that PINS this behaviour itself violates the timeout.

**Concrete evidence.** mgf-fastapi commit at sibling-#2 supervisor adoption (2026-05-26). Reproducer: in a fresh venv with `pytest-timeout`, run `pytest -m "" tests/unit/test_testing.py::TestRunTestApp::test_startup_timeout_raises_timeout_error --timeout=30`. The test exceeds 30s.

**Suggested shape.** Inside `run_test_app`'s context manager exit path: when `startup_timeout_seconds` has fired (or when any exception is propagating through the cleanup), `task.cancel()` the lifespan task explicitly and `await asyncio.wait_for(task, timeout=2.0)` with a short cancellation grace, then `task.exception()` if still incomplete. The 2-second grace gives a polite lifespan a chance to honour CancelledError; anything longer is a sleep that's ignoring cancellation, and we move on.

**Cost if deferred past v1.0.** Low for now — the `@pytest.mark.timeout(120)` on the surfacing test is a stable workaround. Cost is real for consumers writing their own tests with stuck-startup fixtures: their CI gets slower for no diagnostic benefit. Re-open priority bumps if a second consumer files the same friction.

**Decision rationale.** *(Filled when triaged.)*

**Retrospective.** *(Filled when shipped.)*

---

## §3 Reviewer's meta-feedback

### From vm-vmanager-app (CLI/desktop consumer of mgf-common) — 2026-05-18

Cross-cutting observation derived from vm-vmanager-app's `0.34 → 0.38`
catch-up to mgf-common ([commit `212808f`](https://codeberg.org/magogi-admin/vm-vmanager-app/commit/212808f),
2026-05-18) and the federation-contract-conformance audit
([commit `61cf070`](https://codeberg.org/magogi-admin/vm-vmanager-app/commit/61cf070)).
I am NOT an mgf-fastapi consumer — vm-vmanager-app has no web surface
and likely never will. This is a federation-design observation
for the maintainer.

**The observation.** Auditing vm-vmanager-app (a CLI/desktop application
with no FastAPI / HTTP-server / SaaS shape) against mgf-common's
v2.10 standards corpus, the rules namespace that cleanly carved
out was **DEP-\*** (DEPLOYMENT.md). Every DEP rule carries an
explicit `service` applicability tag in
[`mgf-standard/docs/standards/DEPLOYMENT.md`](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/DEPLOYMENT.md)
that let vm-vmanager-app mark them ❌ N/A as a single-row entry in
[`docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`](https://codeberg.org/magogi-admin/vm-vmanager-app/src/branch/main/docs/inprogress/MGF_STANDARDS_CONFORMANCE.md)
rather than paragraph-rationale per rule. This was the single
biggest time-saver in the audit.

**Implication for mgf-fastapi.** As mgf-fastapi layers its own
standards on top of mgf-common (`HealthRouter` v0.7+, the
`mgf-common.idempotency` middleware mentioned in mgf-common
0.37's changelog, eventual rate-limit middleware, etc.),
**tag each new rule with applicability**:

- `web` — applies to projects that import this sibling.
- `service` — applies to long-running server processes.
- `universal` — applies to every consumer regardless of shape.

Today, a CLI consumer who happens to import a single mgf-fastapi
primitive (e.g., the `idempotency` middleware reused as a
function-call wrapper outside an HTTP context) would have to
mentally classify each new mgf-fastapi rule by hand: "does this
apply to me?" An applicability tag turns that classification
into a per-rule grep at audit time. Consumer-side catch-up
tooling can then auto-filter — saves every CLI/desktop consumer
of the federation a manual assessment per audit cycle, and the
cost on the sibling-author side is one column in the rule
table.

mgf-common's DEP-* tagging proved this works. The same convention
applied to every sibling standards doc scales linearly with the
federation.

---

## §4 How consumers submit feedback

1. **Open a Codeberg issue** at
   <https://codeberg.org/magogi-admin/mgf-fastapi/issues> if
   you have a quick question. Maintainer triages.
2. **For a structured pain point**: file a PAPER-NN here as a PR
   that adds the entry to §2.
3. **For AI-agent-driven consumer sessions**: see your project's
   `docs/CLAUDE.md` auto-block (managed by `mgf-common catch-up`)
   — the `feedback-discipline` section spells out where to file
   what.

---

## §5 Cross-references

- [mgf-common/FEEDBACK.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
  — federation-wide PAPERs.
- [mgf-standard/docs/standards/DOCUMENTATION.md](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/DOCUMENTATION.md)
  — FEEDBACK discipline rules.
