# FEEDBACK — mgf-sqlalchemy

> 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-sqlalchemy-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_common/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` — host the RLS-scoped pagination pattern (`tenant_session` + `paginate_cursor`) every multi-tenant consumer copy-pastes

| Field | Value |
|---|---|
| Status | ✅ SHIPPED |
| Severity | 🟢 Nice-to-have |
| Submitter | PlasmaMapper (agent session, 2026-05-27) |
| Submitted | 2026-05-27 |
| Federation paper | [PAPER-94 in mgf-common](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md#paper-94) (MOVED here per DOC-13) |
| Decision | accept the suggested shape: `tenant_session` already shipped (v0.1); add the pure `paginate_cursor` helper. |
| Decided on | 2026-05-27 |
| Shipped in | v0.5.0 (commit [`c593891`](https://codeberg.org/magogi-admin/mgf-sqlalchemy/commit/c593891)) |

**Concern.** A Postgres-RLS multi-tenant app repeats two patterns
across every read route: (1) open a transaction and
`SET LOCAL app.current_tenant = :tid` before any query, and (2)
cursor-paginate by over-fetching `limit + 1` rows, detecting
truncation, and computing `next_cursor`. Both are mechanical and
identical across routes; `mgf-sqlalchemy` is the natural home for
them.

**Why it matters.** PlasmaMapper's re-design Wave 5 extracted a thin
`TenantQueryService` (`.session(tenant_id)` wrapping a
`tenant_session` context manager that issues the `SET LOCAL`) + a
pure `paginate_cursor(rows, *, limit, cursor_of)` helper, and removed
**13 inline `set_config('app.current_tenant', …)` blocks** + the
over-fetch boilerplate across 5 route modules (~60 LoC). The RLS
`SET LOCAL` mechanic in particular is easy to get subtly wrong (must
be `LOCAL` to the transaction, must precede every query) — a
federation-blessed helper would prevent the footgun.

**Concrete evidence.** PlasmaMapper, 2026-05-27. The pattern lives in
`src/plasmamapper/db/tenant.py` (`tenant_session`) +
`src/plasmamapper/services/tenant_query.py` (`paginate_cursor` +
the `TenantQueryService` L2 wrapper).

**Suggested shape.** `mgf-sqlalchemy` ships: (a) a `tenant_session`
async context manager that opens a transaction + issues the
parameterised `SET LOCAL`, and (b) a `paginate_cursor` pure helper
returning `(items, truncated, next_cursor)`. (Note: bespoke cursor
semantics — ascending replay windows, LATERAL joins — should stay
opt-out; the helper covers the common descending-`created_at` case.)

**Decision rationale.** Accepted. Half the request was already
satisfied: `tenant_session` has shipped since mgf-sqlalchemy v0.1 (it
takes an `AsyncSession` and uses `SET LOCAL` with UUID-validated
interpolation — a slightly different, security-hardened shape from
PlasmaMapper's `set_config(..., true)` sessionmaker-wrapping
variant, but the same RLS contract; see `_session.py` SH-01 / RA-01
notes). The genuinely-missing piece was the **pure `paginate_cursor`
helper**, which is now added as a standalone public name.

The `TenantQueryService` wrapper from PlasmaMapper is deliberately
**not** ported — it is an L2 FastAPI-DI singleton (per-app lifespan
construction + dependency injection), which is consumer/framework
shape, not framework-agnostic library shape. mgf-sqlalchemy stays
closed-box (`mgf-common` + `sqlalchemy` only); a consumer assembles
the thin service from `tenant_session` + `paginate_cursor` itself.
`paginate_cursor` uses `TypeVar` rather than PEP 695 `[T, C]`
type-parameter syntax because this sibling targets `requires-python
>=3.11` (PEP 695 is 3.12+); PlasmaMapper's 3.12+ original uses the
newer syntax.

**Retrospective.** Shipped in v0.5.0 (commit `c593891`).
`paginate_cursor` added in `src/mgf/sqlalchemy/_pagination.py`,
exported from `mgf.sqlalchemy.__init__`, documented in
`PUBLIC_API.md`, with 12 unit tests in
`tests/unit/test_pagination.py`. The cursor is computed only on a
returned row, so the dropped `(limit + 1)`-th row's cursor can never
leak; `limit <= 0` raises `ValueError`.

---

## §3 Reviewer's meta-feedback

### From vm-vmanager-app (CLI/desktop consumer of mgf-common, sqlite-only) — 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). I am NOT an mgf-sqlalchemy consumer — vm-vmanager-app uses
sqlite directly (no ORM) for its monitor-history database and
has no SQLAlchemy surface today.

**The observation.** vm-vmanager-app has a sqlite store at
`src/vmanager/monitor/history.py` that grew organically — a
single-file DB with hand-written `CREATE TABLE` / parameterised
inserts / schema-version row + manual migration. When I noticed
mgf-sqlalchemy exists, the question I couldn't answer from the
README was: "is this for me?" — i.e., is this the right home for
typed connection management + simple migration helpers for a
sqlite-only desktop app, or is it FULL SQLAlchemy ORM that
assumes a Postgres-flavored long-lived service shape? vm-vmanager-app's
adoption posture is "adopt only what we actually call" (per
[`docs/inprogress/MGF_ADOPTION.md`](https://codeberg.org/magogi-admin/vm-vmanager-app/src/branch/main/docs/inprogress/MGF_ADOPTION.md));
without a clear scope statement, the safe default is "don't
adopt, keep the bespoke code."

**Implication for mgf-sqlalchemy.** Publish a clear scope
statement at the top of the README:

- **What this is for.** ("Typed SQLAlchemy session management +
  retry-aware engine configuration for SaaS-shaped consumers"
  / "minimal typed connection-context for any consumer with a
  SQL database" / whichever is true.)
- **What this is NOT for.** ("Not for sqlite-only desktop
  apps without an ORM — use stdlib sqlite3 + mgf-common's
  `VersionedFileStore[T]`-shaped wrapper instead.")
- **Scope split (if applicable).** "use mgf-sqlalchemy.simple
  for X" vs "use the full surface for Y" — published in the
  USERS.md or design doc — would prevent both over-adoption
  pressure and "should I have used this?" remorse.

The federation-contract pull now actively pressures consumers
to evaluate every sibling at every adoption gate; a clear scope
statement prevents the evaluation from defaulting to over- or
under-adoption.

---

## §4 How consumers submit feedback

1. **Open a Codeberg issue** at
   <https://codeberg.org/magogi-admin/mgf-sqlalchemy/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_common/src/branch/main/docs/standards/DOCUMENTATION.md)
  — FEEDBACK discipline rules.
