mgf-livepush 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. mgf_livepush-0.1.2/.claude/commands/sync.md +24 -0
  2. mgf_livepush-0.1.2/.claude/settings.json +26 -0
  3. mgf_livepush-0.1.2/.gitignore +11 -0
  4. mgf_livepush-0.1.2/.import_linter_cache/.gitignore +2 -0
  5. mgf_livepush-0.1.2/.import_linter_cache/42b0730c78206e85c00287a310dd9a845ebf6600.data.json +1 -0
  6. mgf_livepush-0.1.2/.import_linter_cache/81fbf6185bfae13a05470e6b0cf08e7a80149eb8.data.json +1 -0
  7. mgf_livepush-0.1.2/.import_linter_cache/CACHEDIR.TAG +3 -0
  8. mgf_livepush-0.1.2/.import_linter_cache/mgf.livepush.meta.json +1 -0
  9. mgf_livepush-0.1.2/.woodpecker.yml +79 -0
  10. mgf_livepush-0.1.2/AGENTS.md +36 -0
  11. mgf_livepush-0.1.2/CHANGELOG.md +100 -0
  12. mgf_livepush-0.1.2/CONTRIBUTING.md +59 -0
  13. mgf_livepush-0.1.2/DEPS.md +54 -0
  14. mgf_livepush-0.1.2/FEDERATION.md +18 -0
  15. mgf_livepush-0.1.2/FEEDBACK.md +90 -0
  16. mgf_livepush-0.1.2/LICENSE +21 -0
  17. mgf_livepush-0.1.2/PKG-INFO +114 -0
  18. mgf_livepush-0.1.2/README.md +77 -0
  19. mgf_livepush-0.1.2/SECURITY.md +109 -0
  20. mgf_livepush-0.1.2/STARTHERE.md +74 -0
  21. mgf_livepush-0.1.2/docs/claude/CLAUDE.md +314 -0
  22. mgf_livepush-0.1.2/docs/inprogress/MGF_STANDARDS_CONFORMANCE.md +166 -0
  23. mgf_livepush-0.1.2/pyproject.toml +173 -0
  24. mgf_livepush-0.1.2/src/mgf/livepush/__init__.py +36 -0
  25. mgf_livepush-0.1.2/src/mgf/livepush/_broker.py +129 -0
  26. mgf_livepush-0.1.2/src/mgf/livepush/_errors.py +31 -0
  27. mgf_livepush-0.1.2/src/mgf/livepush/_slotcap.py +92 -0
  28. mgf_livepush-0.1.2/src/mgf/livepush/_sse.py +117 -0
  29. mgf_livepush-0.1.2/src/mgf/livepush/fastapi.py +50 -0
  30. mgf_livepush-0.1.2/src/mgf/livepush/py.typed +0 -0
  31. mgf_livepush-0.1.2/tests/conftest.py +46 -0
  32. mgf_livepush-0.1.2/tests/test_broker.py +41 -0
  33. mgf_livepush-0.1.2/tests/test_reliability.py +353 -0
  34. mgf_livepush-0.1.2/tests/test_slotcap.py +51 -0
  35. mgf_livepush-0.1.2/tests/test_smoke.py +43 -0
  36. mgf_livepush-0.1.2/tests/test_sse.py +76 -0
  37. mgf_livepush-0.1.2/uv.lock +1003 -0
@@ -0,0 +1,24 @@
1
+ ---
2
+ description: Sync THIS project with the MGF federation (mgf-common + standards) and bring it into conformance
3
+ ---
4
+
5
+ Sync THIS project with the MGF federation. Read the canonical recipe at
6
+ `../mgf-common/docs/recipes/federation-sync-prompt.md` — fall back to the
7
+ installed `mgf-common` package, else its latest PyPI release, if no sibling
8
+ checkout is present — and execute it end-to-end against THIS repo:
9
+
10
+ 1. Confirm context (project identity/role/layer/version, mgf-common pin vs
11
+ latest PyPI, stability posture), then proceed autonomously.
12
+ 2. Sync + catch updates (pin drift, sibling extractions, new/changed
13
+ standards, open FEEDBACK papers).
14
+ 3. Audit conformance against the LIVE `docs/standards/` corpus and the
15
+ federation artifacts — don't work from a remembered rule list.
16
+ 4. Apply fixes on a branch with the federation commit trailers, with tests.
17
+ 5. Run the full gate (ruff · mypy --strict · lint-imports · tests · gitleaks)
18
+ and report honestly.
19
+
20
+ Read-only on mgf-common and the other siblings; no push/merge/version-bump
21
+ unless I explicitly ask. "Already conformant — no change needed" is a valid
22
+ result.
23
+
24
+ $ARGUMENTS
@@ -0,0 +1,26 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(ruff *)",
5
+ "Bash(mypy *)",
6
+ "Bash(pytest *)",
7
+ "Bash(lint-imports *)",
8
+ "Bash(uv *)",
9
+ "Bash(git status*)",
10
+ "Bash(git diff*)",
11
+ "Bash(git log*)",
12
+ "Bash(git add *)",
13
+ "Bash(git commit *)",
14
+ "Bash(mgf-common *)",
15
+ "Read",
16
+ "Edit",
17
+ "Write"
18
+ ],
19
+ "deny": [
20
+ "Bash(rm -rf *)",
21
+ "Bash(git push --force *)",
22
+ "Bash(git reset --hard *)"
23
+ ],
24
+ "defaultMode": "default"
25
+ }
26
+ }
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ build/
5
+ dist/
6
+ *.egg-info/
7
+ .mypy_cache/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ .gates/
@@ -0,0 +1,2 @@
1
+ # Automatically created by Grimp.
2
+ *
@@ -0,0 +1 @@
1
+ {"mgf.livepush._sse":[["asyncio",19,"import asyncio"],["contextlib",20,"import contextlib"],["__future__",17,"from __future__ import annotations"],["mgf.livepush._broker",30,"from mgf.livepush._broker import Broker, Subscription"],["mgf.livepush._errors",25,"from mgf.livepush._errors import CapExceededError"],["mgf.livepush._slotcap",31,"from mgf.livepush._slotcap import SlotCap"],["dataclasses",22,"from dataclasses import dataclass"],["typing",23,"from typing import TYPE_CHECKING"],["collections",28,"from collections.abc import AsyncIterator, Awaitable, Callable"],["logging",21,"import logging"]],"mgf.livepush._slotcap":[["collections",18,"from collections.abc import Callable"],["__future__",12,"from __future__ import annotations"],["logging",14,"import logging"],["typing",15,"from typing import TYPE_CHECKING, Any"]],"mgf.livepush._broker":[["typing",13,"from typing import Any, Protocol, runtime_checkable"],["contextlib",12,"import contextlib"],["__future__",9,"from __future__ import annotations"],["asyncio",11,"import asyncio"]],"mgf.livepush.fastapi":[["__future__",8,"from __future__ import annotations"],["mgf.livepush._sse",18,"from mgf.livepush._sse import SSEStreamService"],["fastapi",36,"from fastapi.responses import StreamingResponse"],["fastapi",15,"from fastapi import Request"],["fastapi",35,"from fastapi import HTTPException"],["typing",10,"from typing import TYPE_CHECKING"],["fastapi",16,"from fastapi.responses import StreamingResponse"],["mgf.livepush._errors",12,"from mgf.livepush._errors import CapExceededError"]],"mgf.livepush":[["mgf.livepush._sse",23,"from mgf.livepush._sse import SSEStream, SSEStreamService, sse_headers"],["__future__",18,"from __future__ import annotations"],["mgf.livepush._broker",20,"from mgf.livepush._broker import Broker, MockBroker, RedisBroker, Subscription"],["mgf.livepush._slotcap",22,"from mgf.livepush._slotcap import SlotCap"],["mgf.livepush._errors",21,"from mgf.livepush._errors import CapExceededError, LivePushError"]],"mgf.livepush._errors":[["__future__",7,"from __future__ import annotations"],["mgf.common",9,"from mgf.common.exceptions import AppError"]]}
@@ -0,0 +1 @@
1
+ {"mgf.livepush.fastapi":[["mgf.livepush._sse",18,"from mgf.livepush._sse import SSEStreamService"],["mgf.livepush._errors",12,"from mgf.livepush._errors import CapExceededError"]],"mgf.livepush._errors":[],"mgf.livepush._slotcap":[],"mgf.livepush":[["mgf.livepush._slotcap",22,"from mgf.livepush._slotcap import SlotCap"],["mgf.livepush._errors",21,"from mgf.livepush._errors import CapExceededError, LivePushError"],["mgf.livepush._broker",20,"from mgf.livepush._broker import Broker, MockBroker, RedisBroker, Subscription"],["mgf.livepush._sse",23,"from mgf.livepush._sse import SSEStream, SSEStreamService, sse_headers"]],"mgf.livepush._sse":[["mgf.livepush._slotcap",31,"from mgf.livepush._slotcap import SlotCap"],["mgf.livepush._broker",30,"from mgf.livepush._broker import Broker, Subscription"],["mgf.livepush._errors",25,"from mgf.livepush._errors import CapExceededError"]],"mgf.livepush._broker":[]}
@@ -0,0 +1,3 @@
1
+ Signature: 8a477f597d28d172789f06886806bc55
2
+ # This file is a cache directory tag automatically created by Grimp.
3
+ # For information about cache directory tags see https://bford.info/cachedir/
@@ -0,0 +1 @@
1
+ {"mgf.livepush._broker": 1779951917.1567435, "mgf.livepush._errors": 1779951628.1901445, "mgf.livepush._sse": 1779951917.1567435, "mgf.livepush": 1779951692.2036455, "mgf.livepush.fastapi": 1779951683.8657117, "mgf.livepush._slotcap": 1779951917.1577435}
@@ -0,0 +1,79 @@
1
+ # Codeberg / Woodpecker CI for mgf-livepush.
2
+ #
3
+ # Federation CI shape (WORKFLOWS.md WF-*): lint, typecheck, a per-version
4
+ # pytest matrix, and dependency + secret scans. Runs on every push / PR.
5
+ # No publish flow yet — when a release is cut, copy the tag-coherence ->
6
+ # smoke -> publish -> verify-pypi chain from mgf-common.
7
+ #
8
+ # Installs [redis] + [fastapi] so the SlotCap / SSE code imports resolve;
9
+ # the suite drives an in-process FakeRedis (tests/conftest.py), so no real
10
+ # Redis service is needed. Each step runs in a fresh python:X-slim container.
11
+ # Validate locally before pushing: CI_LOCAL_REPO=$PWD scripts/ci-local.sh ci
12
+ # (from a mgf-common checkout — see docs/recipes/local-ci-pipeline.md).
13
+
14
+ when:
15
+ - event: push
16
+ - event: pull_request
17
+ - event: manual
18
+
19
+ steps:
20
+ # Lint — ruff + import-linter (layering contract in pyproject, rule DP-01).
21
+ - name: lint
22
+ image: python:3.12-slim
23
+ commands:
24
+ - pip install --quiet --upgrade pip
25
+ - pip install --quiet -e ".[dev,redis,fastapi]"
26
+ - ruff check src tests
27
+ - lint-imports
28
+
29
+ # Typecheck — mypy (config-driven: [tool.mypy] packages = ["mgf.livepush"]).
30
+ - name: typecheck
31
+ image: python:3.12-slim
32
+ commands:
33
+ - pip install --quiet --upgrade pip
34
+ - pip install --quiet -e ".[dev,redis,fastapi]"
35
+ - mypy
36
+
37
+ # Per-version test steps — explicit, NOT a step-level `matrix:` (Woodpecker
38
+ # silently ignores that → ${PYTHON_VERSION} never expands → python:-slim).
39
+ # Coverage gate (config fail_under=80) on the 3.12 job only.
40
+ - name: test-3.11
41
+ image: python:3.11-slim
42
+ commands:
43
+ - pip install --quiet --upgrade pip
44
+ - pip install --quiet -e ".[dev,redis,fastapi]"
45
+ - pytest --tb=short -q
46
+
47
+ - name: test-3.12
48
+ image: python:3.12-slim
49
+ commands:
50
+ - pip install --quiet --upgrade pip
51
+ - pip install --quiet -e ".[dev,redis,fastapi]"
52
+ - pytest --tb=short -q --cov=src/mgf --cov-report=term-missing
53
+
54
+ - name: test-3.13
55
+ image: python:3.13-slim
56
+ commands:
57
+ - pip install --quiet --upgrade pip
58
+ - pip install --quiet -e ".[dev,redis,fastapi]"
59
+ - pytest --tb=short -q
60
+
61
+ # Dependency CVE scan (rule SC-10).
62
+ - name: vuln-scan
63
+ image: python:3.12-slim
64
+ commands:
65
+ - pip install --quiet --upgrade pip
66
+ - pip install --quiet pip-audit
67
+ - pip install --quiet -e ".[dev,redis,fastapi]"
68
+ # Audit the DEPENDENCIES; skip mgf-livepush's own editable install
69
+ # (its current version isn't on PyPI yet). `--strict` is dropped
70
+ # because it conflicts with --skip-editable (strict treats the
71
+ # skipped editable as an un-auditable dep). pip-audit still exits
72
+ # non-zero on any real CVE in the dependency set.
73
+ - pip-audit --skip-editable
74
+
75
+ # Secret scan (rule SC-01) — gitleaks over the working tree + history.
76
+ - name: secret-scan
77
+ image: zricethezav/gitleaks:v8.30.1
78
+ commands:
79
+ - gitleaks detect --source . --no-banner --redact
@@ -0,0 +1,36 @@
1
+ # AGENTS.md — AI agent instructions
2
+
3
+ This file follows the cross-tool `AGENTS.md` convention. It is a
4
+ **thin pointer** managed by `mgf-common catch-up` — do not edit it;
5
+ project-specific agent notes belong in this project's `CLAUDE.md`.
6
+
7
+ **AI agents working in this project — get the canonical briefing:**
8
+
9
+ > Run **`mgf-common agents`** and do exactly what it prints.
10
+
11
+ `mgf-common` is a dependency of this project, so that command is
12
+ always installed — it prints the single, federation-wide agent
13
+ briefing with no setup and no assumptions. It covers the federation
14
+ shape, using the library + its siblings instead of reinventing
15
+ them, the standards + the docs layout, keeping this project in sync
16
+ with `mgf-common catch-up`, and filing feedback in the relevant
17
+ library's `FEEDBACK.md` when you hit friction so the library itself
18
+ can improve.
19
+
20
+ Then read this project's own `CLAUDE.md` (`docs/claude/CLAUDE.md`,
21
+ or `docs/CLAUDE.md` on older layouts) — its auto-managed block
22
+ carries the project-specific lookup.
23
+
24
+ **Fallback if `mgf-common agents` fails to run** (PAPER-78 — usually
25
+ a stale editable install pointing at a renamed source tree): read
26
+ the same text from `mgf-common`'s `STARTHERE.md`, section *"For the
27
+ AI agent in any Magogi project"*. The CLI's output and that
28
+ section are kept identical by `tests/unit/cli/test_agents.py`. Open
29
+ either:
30
+
31
+ - The published source on Codeberg:
32
+ [`mgf-common/STARTHERE.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/STARTHERE.md)
33
+ - The same file in your local mgf-common checkout if you have one
34
+ (typically `~/PycharmProjects/mgf-common/STARTHERE.md`).
35
+
36
+ One message for the whole federation; no per-project variant.
@@ -0,0 +1,100 @@
1
+ # Changelog
2
+
3
+ All notable changes to `mgf-livepush` are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
6
+ the project follows [SemVer](https://semver.org/spec/v2.0.0.html). Per
7
+ the 0.x window ([SemVer 0.x](https://semver.org/#spec-item-4) and rule
8
+ **AP-03** from `mgf-common/docs/standards/API_DESIGN.md`), MINOR
9
+ releases MAY break the public API. Pin tightly:
10
+
11
+ ```toml
12
+ mgf-livepush = ">=0.X.0,<0.Y" # one minor at a time
13
+ ```
14
+
15
+ The release-engineering discipline that produces this changelog is in
16
+ [`mgf-common/docs/standards/RELEASING.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/main/docs/standards/RELEASING.md).
17
+
18
+ ---
19
+
20
+ ## [Unreleased]
21
+
22
+ Nothing yet.
23
+
24
+ ---
25
+
26
+ ## [0.1.2] — 2026-05-28 — federation cohort publish (mgf-common 0.42.x)
27
+
28
+ Federation cohort wave: mgf-livepush accepts **mgf-common 0.42.x** (pin widened to
29
+ `>=0.41,<0.43`) and ships to PyPI, carrying the previously-unpublished 0.1.1 work.
30
+
31
+ ---
32
+
33
+ ## [0.1.1] — 2026-05-28 — test hardening + reliability tests
34
+
35
+ ### Added
36
+
37
+ - Adopt `mgf-test-supervisor` + TS-04 `filterwarnings=["error"]` + a coverage
38
+ gate + the full federation marker taxonomy + a tier-0 smoke set.
39
+ - Reliability tests: SlotCap no-oversell under concurrency (atomic-INCR
40
+ guarantee pinned), fail-open contract, SSE slot-release on cancel.
41
+ - `USERS.md` (DOC-16).
42
+
43
+ ### Notes
44
+
45
+ - Test + dev-config only; no public API or runtime-behaviour change.
46
+
47
+ ---
48
+
49
+ ## [0.1.0] — 2026-05-28
50
+
51
+ > **Maiden voyage** — extracted from PlasmaMapper's Phase-4 push
52
+ > surface after the pre-release core re-design hardened it
53
+ > (heartbeat cadence, slot lifecycle, disconnect cleanup, and the
54
+ > dev/prod-proxy buffering recipe). PlasmaMapper is consumer #1.
55
+ > A `mgf-common` federation sibling under the `mgf.*` namespace.
56
+
57
+ ### Added
58
+
59
+ - **`mgf.livepush.SSEStreamService`** — the SSE streaming
60
+ primitive. `open_stream(...)` acquires a connection slot,
61
+ subscribes to a broker channel, and returns an `SSEStream`
62
+ (`.headers` + `.body`, an async byte iterator) with proxy-safe
63
+ headers already set. Framework-agnostic core — wrap `.body` in
64
+ whatever streaming response your framework provides.
65
+ - **`SSEStream` / `sse_headers`** — the `(headers, body)` result
66
+ plus the proxy-safe response headers (`Cache-Control: no-cache,
67
+ no-transform`, `X-Accel-Buffering: no`) that keep live push
68
+ flowing through buffering proxies.
69
+ - **`Broker` / `Subscription`** — the pluggable pub/sub **broker
70
+ seam** (Protocols). A consumer can swap the backing transport
71
+ (Redis pub/sub for Redis Streams / NATS) without touching the
72
+ SSE machinery.
73
+ - **`RedisBroker`** — the Redis pub/sub broker adapter, behind the
74
+ opt-in `[redis]` extra. Consumers on another broker don't pay
75
+ the `redis` dependency.
76
+ - **`MockBroker`** — an in-process broker adapter for tests; no
77
+ Redis required.
78
+ - **`SlotCap`** — per-key connection-**slot cap** via Redis
79
+ INCR/DECR with a TTL. Fail-open by design; returns
80
+ `429 (Retry-After: 5)` automatically when a per-key cap is full.
81
+ - **`CapExceededError` / `LivePushError`** — typed errors
82
+ (subclasses of `mgf-common`'s `AppError`).
83
+ - **`mgf.livepush.fastapi.sse_streaming_response`** — a one-line
84
+ FastAPI `StreamingResponse` wrapper, behind the opt-in
85
+ `[fastapi]` extra. The core stays framework-agnostic; FastAPI
86
+ lives only inside this adapter (enforced by import-linter).
87
+
88
+ ### Engineering
89
+
90
+ - **`mgf-common>=0.41,<0.42`** runtime dependency. AP-03 0.x
91
+ window pin discipline applies — bump in lock-step with
92
+ mgf-common's next minor.
93
+ - PEP 420 namespace package (no top-level `mgf/__init__.py`); the
94
+ wheel ships `src/mgf/` as-is so `mgf.livepush` coexists as a
95
+ SIBLING (not a subpackage) of `mgf.common` and the other
96
+ federation siblings.
97
+ - import-linter contract keeps the core
98
+ (`_sse` / `_broker` / `_slotcap` / `_errors`) free of any
99
+ FastAPI import — framework code is confined to the
100
+ `mgf.livepush.fastapi` adapter.
@@ -0,0 +1,59 @@
1
+ # Contributing — mgf-livepush
2
+
3
+ `mgf-livepush` is a Magogi Foundation sibling and **inherits the
4
+ cross-project engineering standards** from
5
+ [`mgf-common`](https://codeberg.org/magogi-admin/mgf-common).
6
+ This file is short on purpose; the canonical contributor guide
7
+ is the federation contract.
8
+
9
+ ## First read
10
+
11
+ 1. [mgf-common/FEDERATION.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEDERATION.md) — the contract.
12
+ 2. [mgf-common/docs/standards/](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/standards/) — the rule corpus (currently v2.10).
13
+
14
+ ## Rule families (quick reference)
15
+
16
+ | Family | Covers |
17
+ |--------|-------------------------------------------------------|
18
+ | AP-* | API stability tiers, deprecation cycle |
19
+ | CF-* | Layered config + env-var precedence |
20
+ | DEP-* | Pin discipline, transitive caps |
21
+ | DOC-* | Docs layout (universal-5 + sibling MUSTs) |
22
+ | DP-* | Design principles (frozen types, TZ-aware, …) |
23
+ | EH-* | Typed exception hierarchy |
24
+ | FB-* | Feedback discipline (PAPER-NN flow) |
25
+ | LG-* | Structured logging, redaction, audit emission |
26
+ | REL-* | Build → test → tag → push → upload order |
27
+ | SC-* | Atomic writes, redaction, secret handling |
28
+ | TS-* | Test discipline, four-gate CI |
29
+
30
+ ## Process
31
+
32
+ 1. **Bug / feature:** file in [FEEDBACK.md](FEEDBACK.md)
33
+ (sibling-scoped) — or in
34
+ [mgf-common/FEEDBACK.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
35
+ if cross-cutting (per DOC-13 / FB-04).
36
+ 2. **PR:** branch from main; run four-gate CI locally:
37
+ ```sh
38
+ .venv/bin/python -m ruff check src/ tests/
39
+ .venv/bin/python -m mypy --strict src/
40
+ .venv/bin/python -m pytest
41
+ .venv/bin/python -m coverage report
42
+ ```
43
+ 3. **CHANGELOG.md:** add an `[Unreleased]` entry per
44
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
45
+ 4. **Public-API delta:** update [PUBLIC_API.md](PUBLIC_API.md)
46
+ in the same PR; the pinned test gate will catch a missed entry.
47
+ 5. **Reviewer:** the federation owner (mgf-common maintainer).
48
+
49
+ ## Release flow
50
+
51
+ See [REL-* in mgf-common standards](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/standards/RELEASING.md).
52
+ Short version: bump → regen PUBLIC_API.json → build → test →
53
+ `twine check` → commit → tag → push tag → upload.
54
+
55
+ ## See also
56
+
57
+ - [STARTHERE.md](STARTHERE.md) — entry point
58
+ - [mgf-common/FEDERATION.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEDERATION.md) §7 — feedback discipline
59
+ - [mgf-common/CONTRIBUTING.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/CONTRIBUTING.md) — the canonical contributor guide
@@ -0,0 +1,54 @@
1
+ # DEPS — mgf-livepush's bidirectional dependency manifest
2
+
3
+ > **§1** — what mgf-livepush depends on. **§2** — who depends on
4
+ > mgf-livepush. Knowing both directions makes deprecation,
5
+ > breaking-change blast-radius, and feature-prioritisation tractable.
6
+ >
7
+ > **Per standard DOC-16** (v2.12): every project ships a `DEPS.md`.
8
+ > Replaces the former `USERS.md`.
9
+
10
+ Last verified: 2026-05-28.
11
+
12
+ ---
13
+
14
+ ## §1 What mgf-livepush depends on
15
+
16
+ | Dependency | Window | Why | Tier |
17
+ |--------------|----------------|-----------------------------------------------------------|------------------|
18
+ | `mgf-common` | `>=0.41,<0.42` | `AppError` (SSE/broker error base) + observability + standards CLI | runtime |
19
+ | `redis` | `>=5.0` | the Redis pub/sub broker adapter (`RedisBroker`) | `[redis]` extra (lazy) |
20
+ | `fastapi` | `>=0.110` | the `StreamingResponse` wrapper (`mgf.livepush.fastapi`) | `[fastapi]` extra (lazy) |
21
+
22
+ *Federation siblings depended on: `mgf-common` — the cornerstone
23
+ (typed exceptions / bootstrap / observability). No other `mgf-*`
24
+ sibling is depended on.*
25
+
26
+ Runtime core is kept tight: only `mgf-common` ships transitively into
27
+ every consumer. The Redis and FastAPI adapters live behind extras with
28
+ lazy imports, and the import-linter contract forbids `fastapi` from the
29
+ framework-agnostic core (`_sse` / `_broker` / `_slotcap` / `_errors`).
30
+
31
+ ---
32
+
33
+ ## §2 Who depends on mgf-livepush
34
+
35
+ Consumer projects + federation siblings that import this project.
36
+ (Inverse of each consumer's `MGF_ADOPTION.md`.)
37
+
38
+ | Project / sibling | Location | Import sites | Status | Surfaces used |
39
+ |-------------------|---------------------------------------------|-------------:|-------------------------------------|----------------------------------------------------------------|
40
+ | **PlasmaMapper** | `/home/bassam/PycharmProjects/PlasmaMapper` | 5 | Active — consumer #1 (`mgf-livepush[redis,fastapi]`); newly extracted 2026-05-28 | `SSEStreamService` / `RedisBroker` / `SlotCap` / `CapExceededError` |
41
+
42
+ No federation siblings depend on mgf-livepush.
43
+
44
+ ---
45
+
46
+ ## §3 Maintenance
47
+
48
+ Updated every MINOR release + when a dependency or consumer changes.
49
+ Import-survey methodology:
50
+
51
+ ```sh
52
+ grep -rE "(from|import) +mgf\.livepush" /path/to/each/consumer \
53
+ --include='*.py' | wc -l
54
+ ```
@@ -0,0 +1,18 @@
1
+ # FEDERATION — pointer
2
+
3
+ > This project is a member of the **Magogi Foundation**. The federation
4
+ > contract is **not** maintained here — there is one canonical copy.
5
+ >
6
+ > **Read the contract at:**
7
+ > [`mgf-common/FEDERATION.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEDERATION.md)
8
+ >
9
+ > It is the single source of truth for: required docs layout,
10
+ > conformance levels (L1 / L2 / L3), the federation-first principle,
11
+ > catch-up cadence, and feedback discipline. Per **DOC-17**, siblings
12
+ > and consumers link here rather than copy it.
13
+ >
14
+ > **This project's specifics** live in its own root docs:
15
+ > `README.md` (what it is), `DEPS.md` (what it depends on + who depends
16
+ > on it), `CHANGELOG.md`, `FEEDBACK.md`, and
17
+ > `docs/inprogress/MGF_STANDARDS_CONFORMANCE.md` (declared conformance
18
+ > level + per-rule audit).
@@ -0,0 +1,90 @@
1
+ # FEEDBACK — mgf-livepush
2
+
3
+ > Paper-tracking file for issues, friction, and requests against
4
+ > this project. Consumer projects (and any future adopter) file
5
+ > PAPER-NN entries here.
6
+ >
7
+ > **Where to file what:**
8
+ >
9
+ > - **mgf-livepush-specific** → file HERE.
10
+ > - **Federation-wide** (cross-sibling patterns, standards
11
+ > proposals) → file in
12
+ > [mgf-common/FEEDBACK.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md).
13
+ >
14
+ > **Numbering convention:** paper numbers (`PAPER-NN`) are local
15
+ > to this repo (FB-01). When a paper cross-references a
16
+ > federation-wide paper, the federation number appears in the
17
+ > metadata table as a cross-reference row.
18
+ >
19
+ > **Discipline:** see
20
+ > [mgf-common/docs/standards/DOCUMENTATION.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/standards/DOCUMENTATION.md)
21
+ > §FEEDBACK (rules FB-01..FB-04, DOC-13).
22
+
23
+ ---
24
+
25
+ ## §1 Process
26
+
27
+ 1. **File freely.** Any consumer hitting friction MUST file a
28
+ PAPER-NN under §2. Don't wait for permission; the maintainer
29
+ triages.
30
+ 2. **Status flips** through `OPEN` → `SHIPPED` / `DEFERRED` /
31
+ `WONTFIX` / `MOVED` as the paper resolves. A `SHIPPED` row
32
+ carries a commit-SHA reference + release-version reference.
33
+ 3. **Renumbering** never happens within this repo — papers keep
34
+ their PAPER-NN forever (FB-01).
35
+
36
+ ---
37
+
38
+ ## §2 Open feedback (active)
39
+
40
+ *No papers filed yet. The first paper goes here with the shape:*
41
+
42
+ ```
43
+ #### `PAPER-01` — <one-line title>
44
+
45
+ | Field | Value |
46
+ |---|---|
47
+ | Status | OPEN |
48
+ | Severity | 🟢 Low / 🟡 High / 🔴 Critical |
49
+ | Submitter | <consumer-name> (commit [`<sha>`](<url>)) |
50
+ | Submitted | YYYY-MM-DD |
51
+ | Federation paper | [PAPER-NN in mgf-common](...) (if cross-referenced) |
52
+
53
+ **Concern.** ...
54
+
55
+ **Why it matters.** ...
56
+
57
+ **Concrete evidence.** ...
58
+
59
+ **Suggested shape.** ...
60
+ ```
61
+
62
+ ---
63
+
64
+ ## §3 Reviewer's meta-feedback
65
+
66
+ (none yet — placeholder for cross-cutting observations about
67
+ the FEEDBACK process itself.)
68
+
69
+ ---
70
+
71
+ ## §4 How consumers submit feedback
72
+
73
+ 1. **Open a Codeberg issue** at
74
+ <https://codeberg.org/magogi-admin/mgf-livepush/issues> if you have a
75
+ quick question. Maintainer triages.
76
+ 2. **For a structured pain point**: file a PAPER-NN here as a PR
77
+ that adds the entry to §2.
78
+ 3. **For AI-agent-driven consumer sessions**: see your project's
79
+ `docs/CLAUDE.md` auto-block (managed by `mgf-common catch-up`)
80
+ — the `feedback-discipline` section spells out where to file
81
+ what.
82
+
83
+ ---
84
+
85
+ ## §5 Cross-references
86
+
87
+ - [mgf-common/FEEDBACK.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
88
+ — federation-wide PAPERs.
89
+ - [mgf-common/docs/standards/DOCUMENTATION.md](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/standards/DOCUMENTATION.md)
90
+ — FEEDBACK discipline rules (FB-01..FB-04, DOC-13).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bassam Alsanie and mgf-livepush contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: mgf-livepush
3
+ Version: 0.1.2
4
+ Summary: Realtime push pipeline for mgf-common consumers — SSE streaming, a pluggable pub/sub broker seam (Redis + mock), and per-key connection-slot caps. Sibling of mgf-common under the mgf.* namespace.
5
+ Author: Bassam Alsanie, mgf-livepush contributors
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: fastapi,pubsub,realtime,redis,sse,streaming
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: mgf-common<0.43,>=0.41
20
+ Provides-Extra: dev
21
+ Requires-Dist: import-linter>=2.0; extra == 'dev'
22
+ Requires-Dist: mgf-test-supervisor<0.2,>=0.1.2; extra == 'dev'
23
+ Requires-Dist: mypy>=1.10; extra == 'dev'
24
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
25
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
26
+ Requires-Dist: pytest-timeout>=2.3; extra == 'dev'
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.4; extra == 'dev'
29
+ Provides-Extra: fastapi
30
+ Requires-Dist: fastapi>=0.110; extra == 'fastapi'
31
+ Provides-Extra: redis
32
+ Requires-Dist: redis>=5.0; extra == 'redis'
33
+ Provides-Extra: test
34
+ Requires-Dist: fastapi>=0.110; extra == 'test'
35
+ Requires-Dist: redis>=5.0; extra == 'test'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # mgf-livepush
39
+
40
+ Realtime push pipeline for `mgf-common` consumers: an SSE streaming
41
+ primitive, a pluggable pub/sub **broker seam** (Redis adapter + an
42
+ in-process mock), and a per-key connection-**slot cap**. A sibling of
43
+ `mgf-common` under the `mgf.*` namespace.
44
+
45
+ Extracted from PlasmaMapper's Phase-4 push surface after the
46
+ pre-release core re-design hardened it (heartbeat cadence, slot
47
+ lifecycle, disconnect cleanup, and the dev/prod-proxy buffering recipe
48
+ below). The broker seam means a consumer can swap Redis pub/sub for
49
+ Redis Streams / NATS without touching the SSE machinery.
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ pip install "mgf-livepush[redis,fastapi]" # adapters are opt-in extras
55
+ ```
56
+
57
+ ## Quickstart (FastAPI)
58
+
59
+ ```python
60
+ from redis.asyncio import Redis
61
+ from mgf.livepush import SSEStreamService, RedisBroker, SlotCap
62
+ from mgf.livepush.fastapi import sse_streaming_response
63
+
64
+ redis = Redis.from_url("redis://localhost:6379/0")
65
+ sse = SSEStreamService(RedisBroker(redis), SlotCap(redis, cap=20))
66
+
67
+ @router.get("/events/stream")
68
+ async def stream(request: Request):
69
+ # 429 (Retry-After: 5) automatically when the per-key cap is full.
70
+ return await sse_streaming_response(
71
+ sse, request, channel=f"events:{tenant_id}", slot_key=str(tenant_id)
72
+ )
73
+ ```
74
+
75
+ Publisher side, anywhere:
76
+
77
+ ```python
78
+ await RedisBroker(redis).publish(f"events:{tenant_id}", json.dumps(event))
79
+ ```
80
+
81
+ Framework-agnostic core: `open_stream(...)` returns an `SSEStream`
82
+ (`.headers` + `.body` async byte iterator) — wrap `.body` in whatever
83
+ streaming response your framework uses. Tests use `MockBroker` (no
84
+ Redis).
85
+
86
+ ## The dev/prod-proxy SSE recipe (read this — it's the tarpit)
87
+
88
+ A FastAPI `text/event-stream` endpoint behind a proxy (SvelteKit `vite`,
89
+ a `+server.ts` `[...path]` forward, nginx, AWS ALB) will silently
90
+ **buffer** the body and break your latency budget even though the API
91
+ side is correct. To get live push through a proxy:
92
+
93
+ 1. **Response headers (set by this lib):** `Cache-Control: no-cache,
94
+ no-transform` + `X-Accel-Buffering: no`. If your proxy gzips, also
95
+ force `Content-Encoding: identity` on the proxied response.
96
+ 2. **Node `fetch` forwarding** of a streamed body needs `duplex: 'half'`.
97
+ 3. **Playwright:** `waitForLoadState('networkidle')` *never fires* with
98
+ an open `EventSource` (the long-lived connection counts as in-flight)
99
+ — wait on a concrete readiness signal instead.
100
+ 4. **Playwright:** `webServer` spawns *before* `globalSetup`, so any env
101
+ the proxy needs must be declared statically in `webServer.env`, not
102
+ threaded from `globalSetup`.
103
+
104
+ ## Public surface
105
+
106
+ | Name | What |
107
+ |---|---|
108
+ | `SSEStreamService` | acquire slot → subscribe → stream (`open_stream`) |
109
+ | `SSEStream` / `sse_headers` | the `(headers, body)` result + the proxy-safe headers |
110
+ | `Broker` / `Subscription` | the pub/sub port (Protocols) |
111
+ | `RedisBroker` / `MockBroker` | adapters (`[redis]` extra / in-process) |
112
+ | `SlotCap` | per-key INCR/DECR connection cap, fail-open, TTL |
113
+ | `CapExceededError` / `LivePushError` | typed errors (subclass `AppError`) |
114
+ | `mgf.livepush.fastapi.sse_streaming_response` | one-line FastAPI wrapper (`[fastapi]` extra) |