Repository navigation
Conversation
…e their body completes Complete the #1091 request-boundary acceptance cases and document the bounded HTTP admission contract.
The request-boundary fix changes bound runtime source, so publish specification coverage 69.0.0 and formal semantic validation 70.0.0. Replayed outcomes and claim limits are unchanged.
This was referenced Oct 9, 2026
Name the existing size-guard tests in the #1091 module docstring and say why they stay. Drop the 16 parametrized cases whose input repeated another case under a misleading id, require exactly one response start per exchange, and limit the guard's log to the fixed audit-failure message. The guide now says that a route runs once the adapter has read the last body chunk, and that a server can report a disconnect after the client has sent the whole body.
doublewhy
marked this pull request as ready for review
October 9, 2026 15:01
9 of 11 tasks
…iption The fixed Content-Length variants in the #1091 request-limit matrices are now named by the value they declare (declares-zero, declares-limit, declares-past-limit) instead of by how that value compares with the body. The old overstated-to-limit name described a 16-byte declaration that understates each 17-byte body. The disconnect cases use the same names. GET /openapi.json joins the composed-adapter routes, so the overflow and disconnect cases now also cover an API description route.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
#1091 asks for complete acceptance cases and documentation for the control-plane HTTP request-size boundary,
RequestSizeLimitMiddleware. The new cases drive the middleware, and the composed P2 adapter, through the public ASGI interface with a scripted server channel. They found one defect: a request whose client disconnected before its body was complete still reached the app. Ondev, aPOST /workflows/reconcile-timeoutsthat declaredContent-Length: 10and then disconnected ranreconcile_workflow_timeouts, aGET /snapshotranget_snapshot, and body routes answered the departed client with400. The middleware also stored the partial bytes inscope["state"]["raw_body"].The middleware now treats a disconnect before the final body frame as an abandoned request. No route runs, nothing is sent, and no partial body is kept. Accepted, oversized and invalid-length requests behave as before. A new "Bound request bodies" section in the public control-plane guide covers the bounded routes, the limit setting, the counting point, buffering and replay, disconnects, rejections, and the ASGI server and proxy duties. It promises no streaming pass-through. The guard change moves the bound implementation digest, so both research evidence captures are republished.
Existing cases. Done-when 1 starts with an inventory. These size-guard tests were already on
dev:test_issue_1093_request_rejection_offload.py: limit and pending-bound validation, non-HTTP pass-through, coalesce then delegate, 100 empty frames, disconnect before the body (renamed here, see Changes), streamed overflow without a header, a 1 MiB chunk refused before it is copied, the exact stream boundary, seven ambiguousContent-Lengthvalues, audit offload and saturation, and anOSErroraudit failure for a400and a declared413.test_runtime_control_plane_api.py: declared overflow (:1601), invalidContent-Length(:1621), chunked overflow (:1648), and no reads after the crossing chunk (:1804).test_issue_1186_control_plane_recovery_operations.py: the audit-failure log omits the error and the request path (:946), and an oversized method is still audited (:975).test_issue_1359_runtime_api_trust_boundary.py: a413carriesno-store(:593), andmax_request_bytesmust be anint(:837).The new module adds what those lack: misleading
Content-Lengthvalues, empty and one-byte frames at the middleware and at the composed adapter, disconnects, seven route kinds with and without a bearer token, intact delivery to an endpoint, and three audit-failure error types. A few matrix cells repeat the shape of an old case, such as one over-limit chunk with no header, so that the grid has no holes. The old tests are kept on purpose. Most check something the new module does not, such as theraw_bodystate, the copy guard, the parser variants, a failing audit store, a run throughTestClient, read counts at the composed adapter, or the request-path redaction. Two are close overlaps:test_request_size_middleware_rejects_stream_that_exceeds_limit_without_header(theover-limit-absentcell) andtest_request_size_middleware_does_not_retain_empty_transport_messages(100 empty frames, where the matrix uses up to 3). The matrix repeats their shapes with more assertions. They stay, because removing #1093's tests is outside #1091's scope.#1444 (the missing profile nonclaims) is stacked on this branch at
11854670, so it carries this branch's first three commits. This branch's fourth commit,03709bfa, changes only the new test module, which #1444 does not touch. #1443 (the API-404 clause-to-profile map) is based ondevand does not depend on this branch.Requirement UIDs
API-404Related Issues
Closes #1091
ADR Impact
Changes
raes_runtime/control_plane_api_guards.py:RequestSizeLimitMiddlewarereads the body through_receive_bounded_body(), which returns the complete body,OVERSIZED, orABANDONED. Anhttp.disconnectbefore the finalhttp.requestframe returnsABANDONED, so the middleware sets noraw_body, calls no app and sends no response. Before, it replayedhttp.disconnectto the app with the partial bytes inraw_body. The 400/413 paths, the single replayed message and the delegation of laterreceive()calls are unchanged.tests/test_issue_1091_request_limit_boundary.py(new, 138 cases, ASGI messages only, no framework-private body state). The module docstring names the existing size-guard modules. No two cases of one test share an input, and every case that expects a response requires exactly onehttp.response.start:Content-Lengthvariants: absent, truthful, and the fixed values0,16(the limit) and17. The ids name a fixed value by what it declares (declares-zero,declares-limit,declares-past-limit), because one value can understate one body and overstate another. The 7 pairs where a fixed value equals the true length are left out, because they repeat the truthful case. An admitted body is replayed once with its exact bytes, and later reads reach the server. A refused one gets the stable413after exactly 0 reads for a declared overflow, or at the crossing frame otherwise. Refusal never calls the app and records onehttp-request-rejectedaudit.declares-zero,declares-limit) at the middleware, and 7 routes × 2 points on the composed adapter. No endpoint runs, nothing is sent, and nothing is audited.GET /openapi.json, public probe, administrative read, body and bodyless mutation, operator resolution, unrouted path), with and without a bearer token. Each gets one413{"detail":"request too large"}withCache-Control: no-storeand one rejection audit, and no endpoint runs.Content-Lengthvariants (absent, truthful,declares-zero,declares-limit), less the 9 that repeat another case (one-byte frames of the empty body, and a fixed value equal to the true length). Each reachescancel_workflowwith the exactreason.OSError,RuntimeErrorandsqlite3.OperationalError, each carrying a sentinel, for the invalid-length, declared-overflow and streamed-overflow rejections. The status, body and headers stay stable and no endpoint runs. The sentinel appears in neither the response nor the captured logs. The guard's logger records only the fixed failure message, and no record carries a traceback.tests/test_issue_1093_request_rejection_offload.py:test_request_size_middleware_accepts_disconnect_before_bodypinned the old dispatch. It is renamed..._abandons_disconnect_before_bodyand now asserts that no route runs and noraw_bodyis set.docs/public/guides/control-plane.md: new "Bound request bodies" section. The check covers every method and path, including probes, API description routes and unrouted paths. It documentsControlPlaneSecurityConfig.max_request_bytes(default 1,000,000), declared-length and counted-byte refusal, whole-body buffering with no streaming to routes, rejection bodies,no-storeand audit-failure behavior, and server and proxy duties. On disconnects, it says a route runs only once the adapter has read the last body chunk. It also says the server can report a disconnect even after the client has sent the whole body, so each mutation should carry anIdempotency-Keythat is reused on retry. The locked uvicorn 0.42.0 does this:receive()returnshttp.disconnectonce the client has gone, even with body bytes buffered (uvicorn/protocols/http/h11_impl.py:541-542,httptools_impl.py:573-574).docs/requirements/API-404/requirement.md: IMPLEMENTS, DOCUMENTS and TESTS lines for the guard, the guide and the new module, in the existing Bind bearer authorization to target scope: delivered #1090/Complete HTTP request-limit boundary tests and documentation #1091/fix(runtime): offload backend execution from the event loop #1093 block.execution-snapshot-v69.json,analysis-v69.json, issue-1091 bundle) replays the retained matrix against this branch's source.execution-snapshot-v70.json,analysis-v70.json,retest-v70.json) replays the retained formal cases with baseline 69.0.0 and no deviation.tools/check_specification_coverage.py,tools/formal_semantic_validation/, and the three evidence test modules. Both research indexes record the new releases.Test Plan
All results below are for head
03709bfa.pytest test_issue_1091_request_limit_boundary.py test_issue_1093_request_rejection_offload.pypassed (159 passed). With-m integrationboth modules select nothing (159 deselected, exit 5), because neither has integration cases.With only
control_plane_api_guards.pyrestored toorigin/dev, 26 of the 138 new cases fail: the 12 middleware and 14 composed-adapter disconnect cases. The renamed #1093 test fails as well. The composed-adapter failures showget_snapshot,reconcile_workflow_timeoutsandcontrol_plane_readinesscalls,400 There was an error parsing the bodyresponses on the body routes, a404on the unrouted path, and a200carrying the API description onGET /openapi.json.Collected ids: 146 at
3bd15898, 130 at11854670(16 repeats dropped, none added or renamed), and 138 at03709bfa.03709bfarenames 36 ids, adds 8 and drops none. In the two matrices,understated,overstated-to-limitandoverstated-past-limitbecomedeclares-zero,declares-limitanddeclares-past-limit(17 middleware ids, 11 delivery ids). The middleware disconnect idsunderstatedandat-limitbecomedeclares-zeroanddeclares-limit(8 ids). The 8 new ids are the API description route's 6 overflow and 2 disconnect cases. A script over the module's own parameters finds no two cases with the same frames and declared length in either matrix, and no two composed routes with the same method and path.Two temporary guard mutations, reverted afterwards, show that the tighter assertions catch defects that the module at
3bd15898missed. The first addsawait send({"type": "http.response.start", "status": 413, "headers": []})for status413just before theJSONResponsein_reject(control_plane_api_guards.py:245), so every413carries a second response start. It fails 65 cases: 17 middleware refusals, 42 composed-adapter refusals, and the 6 audit-failure cases that expect a413. The module at11854670fails 59, because it had no API description cases. The module at3bd15898passes all 146 of its cases. The second mutation repeats the_LOGGER.errorcall in_reject's audit-failure branch. It fails the 9 audit-failure cases, and the module at3bd15898passes all 146.The other suites that build the size guard or the HTTP adapter (
test_issue_1186_control_plane_recovery_operations.py,test_issue_1359_runtime_api_trust_boundary.py,test_runtime_control_plane_api.py) passed (302 passed). Their-m integrationrun selects nothing (302 deselected). The API-404 drift tests (test_issue_1185_api_404_profile_alignment.py,test_issue_1189_control_plane_profile_declarations.py) passed (20 passed), including the check that every local traceability artifact exists.Coverage of
raes_runtime.control_plane_api_guardsfrom the two boundary modules reaches every line of the changed_handle_http,_receive_bounded_bodyand_replaypaths. The file's remaining misses are the seal and no-store middleware branches and an unreachableint()fallback. All of these predate this change.Evidence:
tools/check_specification_coverage.pyandtools/check_formal_semantic_validation.pyboth pass. Neither11854670nor03709bfachanges an input the captures bind (no*.pyunderimplementations/python/packages,pyproject.tomloruv.lock), so releases 69.0.0 and 70.0.0 still hold. The three evidence modules pass in the default lane (209 passed, 19 deselected) and with-m integration(19 passed, 209 deselected).nox -s verify-fast-feedback -- --base-rev origin/devpassed: hygiene, policy, lint and the 5 changed pytest modules (368 passed, 19 deselected).nox -s lintpassed.make policypassed; requirement governance is skipped there because the branch name and scope files select no UID, as on the earlier API-404 issue branches. Forcing it withtools/check_requirement_governance.py --base-rev origin/dev --requirement-uid API-404also exits 0.nox -s docs-localpassed (Vale 0 errors in 22 files, Sphinx-WHTML, public output inventory).Ground Control Checks
Traceability
Checklist
fix(runtime)because abandoned requests no longer reach routesDocumentation
Updated:
docs/public/guides/control-plane.md("Bound request bodies").