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.
- mgf_livepush-0.1.2/.claude/commands/sync.md +24 -0
- mgf_livepush-0.1.2/.claude/settings.json +26 -0
- mgf_livepush-0.1.2/.gitignore +11 -0
- mgf_livepush-0.1.2/.import_linter_cache/.gitignore +2 -0
- mgf_livepush-0.1.2/.import_linter_cache/42b0730c78206e85c00287a310dd9a845ebf6600.data.json +1 -0
- mgf_livepush-0.1.2/.import_linter_cache/81fbf6185bfae13a05470e6b0cf08e7a80149eb8.data.json +1 -0
- mgf_livepush-0.1.2/.import_linter_cache/CACHEDIR.TAG +3 -0
- mgf_livepush-0.1.2/.import_linter_cache/mgf.livepush.meta.json +1 -0
- mgf_livepush-0.1.2/.woodpecker.yml +79 -0
- mgf_livepush-0.1.2/AGENTS.md +36 -0
- mgf_livepush-0.1.2/CHANGELOG.md +100 -0
- mgf_livepush-0.1.2/CONTRIBUTING.md +59 -0
- mgf_livepush-0.1.2/DEPS.md +54 -0
- mgf_livepush-0.1.2/FEDERATION.md +18 -0
- mgf_livepush-0.1.2/FEEDBACK.md +90 -0
- mgf_livepush-0.1.2/LICENSE +21 -0
- mgf_livepush-0.1.2/PKG-INFO +114 -0
- mgf_livepush-0.1.2/README.md +77 -0
- mgf_livepush-0.1.2/SECURITY.md +109 -0
- mgf_livepush-0.1.2/STARTHERE.md +74 -0
- mgf_livepush-0.1.2/docs/claude/CLAUDE.md +314 -0
- mgf_livepush-0.1.2/docs/inprogress/MGF_STANDARDS_CONFORMANCE.md +166 -0
- mgf_livepush-0.1.2/pyproject.toml +173 -0
- mgf_livepush-0.1.2/src/mgf/livepush/__init__.py +36 -0
- mgf_livepush-0.1.2/src/mgf/livepush/_broker.py +129 -0
- mgf_livepush-0.1.2/src/mgf/livepush/_errors.py +31 -0
- mgf_livepush-0.1.2/src/mgf/livepush/_slotcap.py +92 -0
- mgf_livepush-0.1.2/src/mgf/livepush/_sse.py +117 -0
- mgf_livepush-0.1.2/src/mgf/livepush/fastapi.py +50 -0
- mgf_livepush-0.1.2/src/mgf/livepush/py.typed +0 -0
- mgf_livepush-0.1.2/tests/conftest.py +46 -0
- mgf_livepush-0.1.2/tests/test_broker.py +41 -0
- mgf_livepush-0.1.2/tests/test_reliability.py +353 -0
- mgf_livepush-0.1.2/tests/test_slotcap.py +51 -0
- mgf_livepush-0.1.2/tests/test_smoke.py +43 -0
- mgf_livepush-0.1.2/tests/test_sse.py +76 -0
- 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 @@
|
|
|
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 @@
|
|
|
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) |
|