Skip to content

Applications — current state

Version: 0.4 · Last Updated: 2026-09-08 · Status: 🔴 evidence refreshed; design ratification unchanged

Verified against source revision 2465fcc (develop baseline). Test references below identify the executable contracts; they are not a new coverage percentage.

Application contract and routing

BaseApplication provides code, mount, exactly-once server ownership, application-relative configuration, no-op lifecycle hooks and the ASGI callable contract. mount="" means the root; only None falls back to the code. app_snapshot and app_panel supply generic monitor contributions. The monitor also accepts an optional panel_source supplied by an application.

RoutedApplication combines that contract with RoutingClass. It plugs auth at construction and arms the server's fixed pydantic/openapi plugins on the first router access after attachment. Branches use add_branches; the existing instance-form consumers do not demonstrate the full declarative cls/params destination described in the decisions.

Claim anchors: BaseApplication, app_snapshot, app_panel, RoutedApplication.

Request parsing and handler arguments

Request.init owns headers, cookies, query parsing and body consumption. It drains all body chunks even without a Content-Type. JSON, XML and MessagePack are decoded with TYTX; URL-encoded fields use from_qs. Multipart text fields are hydrated and files become UploadedFile values (name, filename, content_type, data); repeated field names produce a list. Unknown media and absent Content-Type retain raw bytes. The request body is buffered in full.

handler_kwargs starts with query values. Form fields override colliding query values; decoded non-form data becomes body_data, and opaque bytes become body_raw. bind_kwargs spreads a decoded dict over declared parameters unless the handler accepts body_data or **kwargs. A declared, unannotated _request receives the live request in every RoutedApplication, not only _server. This common opt-in seam was explicitly approved in N37 on 2026-09-06; its relationship to the older D31 effects target remains recorded in the decision follow-up.

Claim anchors: Request, init, UploadedFile, content_type, handler_kwargs, bind_kwargs, RoutedApplication.

Dispatch, authorization and failures

Async handlers run on the event loop. Sync handlers use server.run_sync and run route_cleanup on that same thread even on failure. Router misses yield 404, anonymous access to a ruled route yields 401, and insufficient tags yield 403. The outer error middleware may turn a browser's 401 into a login redirect.

Signature mismatch yields 400. Values rejected by Pydantic after successful binding yield 422. An exception from the handler body, including TypeError, propagates to the error middleware as 500. The Response.set_error helper's standalone exception table must not be substituted for that dispatch contract.

Claim anchors: run_sync, route_cleanup, Response, set_error.

Behavior evidence: __call__, make_callable.

Buffered responses, streams and database cleanup

Response emits one start and one body message. set_result serializes collections as JSON or the requested TYTX transport, handles text/bytes/paths, and applies node metadata. A returned StreamingResponse instead emits chunks without collecting them; SseStream provides framing, keepalive and iterator cleanup. This core streaming path does not make the SPA worker channel stream: AsgiSeam collects a hosted response into one reply.

Request.db resolves the configured database handler and registers its closeConnection cleanup on the current request item. get_db(name) only looks up the handler. The handler-specific thread cleanup hook is a separate seam.

Claim anchors: Response, set_result, StreamingResponse, SseStream, AsgiSeam, Request, db, get_db.

WebSocket and package boundaries

Applications can receive WSX messages as synthetic HTTP scopes with method WSK; the server can also hand an application its raw serve_websocket scope. The HTTP middleware does not run per WSX message.

OpenApiApplication, McpApplication, McpOpenApiApplication, ServerApplication and ConfigurationProfilesApplication live in the core. SpaApplication and its grammar live in genro_asgi_multiworker_spa, shipped by the same distribution. The old SPA import paths have no compatibility re-export. Dynamic movability/removal/failure declarations remain design distance.

Behavior evidence: on_websocket, _call_application, SpaApplication.

Source and test evidence