Docs carry the shape of the system, not its status

MIZAN.md's phase-numbered implementation order, PRODUCT_ARCHITECTURE's
"Deferred until Render revenue funds it" and "Shipped in", PSR_VS_EDGE's
current-state section, and the READMEs' passing-test counts were all reporting
where the work stood rather than what the system is. OWED_SURFACE keeps its
subject — surface that is specified but unbuilt — stated as the shape each unit
owes.

The channel sections follow the renamed slots: Params / ClientMessage /
ServerMessage, and Channel as the base class on both backends.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-27 14:03:29 -04:00
parent 3aafec6dd4
commit e00b3a177e
10 changed files with 243 additions and 213 deletions

View File

@@ -5,11 +5,10 @@ function. Typed React client generated. Invalidation automatic.
## Scope
mizan-fastapi targets the **AFI-common subset** RPC dispatch, context
bundling, JSON-body invalidation, and auth gating. Forms, Channels, Shapes,
and SSR are out of scope for the FastAPI adapter — FastAPI projects use
native equivalents (Pydantic, native WebSockets, ORM-of-choice, FastAPI's
own SSR ecosystem).
mizan-fastapi's surface is RPC dispatch, context bundling, JSON-body
invalidation, auth gating, and channels over a multiplexed WebSocket. Forms,
Shapes, and SSR sit outside that surface — a FastAPI project reaches for its
own native equivalents (Pydantic, ORM-of-choice, FastAPI's SSR ecosystem).
## Install
@@ -29,11 +28,13 @@ from mizan_fastapi import (
mizan_exception_handler,
mizan_validation_handler,
router as mizan_router,
ws_router,
)
app = FastAPI()
app.include_router(mizan_router, prefix="/api/mizan")
app.include_router(ws_router, prefix="/api/mizan")
app.add_exception_handler(MizanError, mizan_exception_handler)
app.add_exception_handler(RequestValidationError, mizan_validation_handler)
```
@@ -82,9 +83,56 @@ a dedicated `clients.py` imported during startup.
@client(rev=2) # cache revision (busts on bump)
```
`websocket=True`, Forms, and Channels parameters are accepted by the
decorator (they're a `mizan-core` primitive) but ignored by mizan-fastapi —
those features only have effect when paired with mizan-django.
Forms parameters are accepted by the decorator (they're a `mizan-core`
primitive) and carry no meaning to this adapter.
## Channels
A channel is a named fan-out over the WebSocket `ws_router` serves. Subclass
`Channel`, declare whichever payload models the channel carries, and register
it. The three model names are read from the client's side: `Params` keys the
fan-out, `ClientMessage` travels up, `ServerMessage` travels down.
```python
from mizan_fastapi import Channel, register_channel
from pydantic import BaseModel
class Chat(Channel):
class Params(BaseModel):
room: str
class ClientMessage(BaseModel):
text: str
class ServerMessage(BaseModel):
user: str
text: str
def authorize(self, params: Params) -> bool:
return True
def receive(self, params: Params, msg: ClientMessage) -> ServerMessage:
return self.ServerMessage(user="anon", text=msg.text)
register_channel(Chat, "chat")
```
Server code pushes to a group from anywhere:
```python
await Chat.push(Chat.ServerMessage(user="system", text="hello"), room="general")
```
Registered channels contribute to the exported IR, so codegen emits the
`<Pascal>Params` / `<Pascal>ClientMessage` / `<Pascal>ServerMessage` types and
the matching frontend hook.
Group membership lives in the process that holds the socket, so a push reaches
only the subscribers attached to that process. Fan-out that spans processes is
a shared broker in front of `broadcast`.
## Auth integration
@@ -169,8 +217,6 @@ python -m mizan_fastapi.ir <module>
Imports the named module (which must register every `@client` function as
import-time side effects), then prints the Mizan KDL IR to stdout.
Mirrors mizan-django's `manage.py export_mizan_ir` so the codegen consumes
either backend the same subprocess way.
## Architecture
@@ -183,4 +229,4 @@ emit equivalent schemas for the same registered functions. See
A live e2e harness exercises this adapter end-to-end at
`examples/fastapi-react-site/` (real Chromium → React with generated hooks
→ FastAPI server, 14/14 Playwright tests).
→ FastAPI server, driven by Playwright).