Integrate all 62 open PRs - #67
Merged
Merged
Conversation
axum applies a 2 MiB `DefaultBodyLimit` to every route unless a layer says otherwise, and nothing here ever did. That default was therefore the real ceiling on everything a caller can hand this service, and it was invisible: the refusal reads `Failed to buffer the request body: length limit exceeded`, which names neither the limit nor the fact that it belongs to us and not to the vendor behind the graph. A caller spent a week failing against it. Measured from their side on 2026-09-04, from both sides of the wall: pushes of 11,408 / 10,387 / 8,976 records went through, and pushes of 12,000 and 16,096 did not. Divide and 2 MiB is exactly where those cross — their payloads run about 130 to 175 bytes a record depending on the vendor. Worse than the refusal was what it cost them: a size refusal is deterministic, so their retry budget was spent re-sending identical bytes and the work was then parked, silently, for 2,683 coordinates. 8 MiB, as GATE_MAX_PUSH_BODY_BYTES. The ceiling on the ceiling is memory rather than taste: a body limit is a per-request buffer and this service runs with a 512 MiB limit, so four times the old value keeps a large caller comfortably inside it while a burst of ten concurrent pushes still costs under a sixth of the pod. The env override is floored at axum's own default so that a typo cannot make the service refuse bodies it accepted before anybody set the variable. PUSH ROUTES ONLY, applied per route rather than as one layer on the Router. A push is a batch — one request stands for as many items as the caller grouped — and the honest bound on it is memory. A graph declaration, a breaker poke and a console read are documents, none has ever come near 2 MiB, and raising their ceiling would buy nothing while letting anybody hand a 512 MiB pod a body per request. The tests go through the ROUTER rather than reading the knob back, because the knob was never the thing that was wrong: a test asserting `knobs().max_push_body` would have passed just as happily on the day a caller was being refused. Mutation-checked: dropping the layer from a push route fails the case that a 3 MiB push is accepted, and moving it to the whole Router fails the case that a document route still refuses one. No broker is needed and none is reached — the extractor rejects before the handler runs. Claude-Session: https://claude.ai/code/session_01RJHziF1EPc5fH7Kd7WRdgk
#61 made Depths::pending and pending_of_group return Result and added ? at every caller on its branch. The three per-node backlog reads in api::declare::view were added by a PR outside #61's stack, so #61 never saw them; each pairwise merge was clean and the combined tree did not compile. view already returns queen_mq::Result, so these propagate like the others. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # crates/server/src/api/console.rs # crates/server/src/history.rs
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The workflow only fired for PRs whose base was master, so a PR stacked on another feature branch was never checked; 23 of the 62 open PRs had no run at all. push stays master-only. #4 fixes docker-build.yml's main->master but does not touch this. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
#66 keeps a stored document running when it breaks a rule added after it was written, refusing only for rules that leave no plan to run. #47's graph-workers was in the kept half: a stored document resolving to more than MAX_GRAPH_WORKERS would restart with a WARN. That is not a graph kept running. Each worker pre-allocates a task and a broker long-poll, so such a plan can exhaust the replica before it serves anything and takes every other graph on it down as well; one graph offline with a WARN naming the rule is the better failure. Maintainer's call, reversing the PR's placement; the pinning test moves the rule across. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 7, 2026
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.
All 62 open pull requests (#3–#66, minus the closed #7 and #33), merged onto
master@ 62610b0 in one branch and verified end-to-end against broker queen 1.5.0 + Postgres 16.Recipe, evidence and every hand-resolution, on one page: https://claude.ai/code/artifact/11d6114f-7f10-4e11-9ef9-b392f95a0848
What is here
39a8d4d. Once this lands, GitHub should mark each of the 62 as merged on its own.History::flow, Fix silent history read failures #60 wraps it inResult; the reconciled signature carries both.Verification (on
39a8d4d, broker and Postgres recreated from scratch, as CI's services are)npm ci && npm run buildcargo clippy --workspace --all-targets -- -D warningscargo test --workspace -- --include-ignored,GATE_TEST_REQUIRE_LIVE=1cargo +1.88 check --workspace --all-targets(MSRV)cargo build --release --workspacecargo fmt --all --checkBaseline for comparison:
masteritself against 1.5.0 is 41/41 live in 348s. The live count grows because the PRs bring their own tests, which ran together for the first time here. Note the CI workflow still pinsqueen:1.0.5.Two clean merges that were wrong, fixed on the branch
Depths::pending/pending_of_groupreturnResultand added?at every caller on its branch; fix(console): show live node backlog in graph detail #5's three per-node backlog reads inapi::declare::viewwere on a branch Stop reporting unknown queue depth as zero #61's stack never contained. Sixty-one PRs merged without a single conflict marker and did not compile.view()already returnsqueen_mq::Result, so the three reads propagate.#[test]lost in my fix(budgets): make counter keys collision-free #38 resolution. Git's hunk began one line below the shared attribute; clippy-D warningscaught the bare fn as "never used". Restored.Three decisions the audit left open, taken
graph-workersis fatal for a stored document. fix(declare): keep a stored graph running when a later rule would refuse it #66 kept a stored document running for every rule outside its fatal list, including fix: refuse graphs that can exhaust consumer workers #47's worker cap; a plan that would exhaust the replica before it served anything is not a graph kept running and takes every other graph on the replica down with it. Reverses the placement in fix(declare): keep a stored graph running when a later rule would refuse it #66's pinning test — a maintainer's call, stated in the commit.test.ymlruns on every PR base (pull_request.branches: ['**'];pushstays master-only). 23 of the 62 PRs had no CI run because their base was another feature branch. chore: standardize JavaScript runtime on Node 24 #4 fixesdocker-build.yml'smain→masterbut not this.cargo test -p gate-coreon fix(validation): require a node-wide breaker budget #34 passes — while the migration-runs-once argument, which is the real reason for fix(declare): keep a stored graph running when a later rule would refuse it #66, is kept through to its consequence.Where to look when reviewing
Two resolutions combine intent rather than text and deserve the authors' eyes: #47 × #66 in
crates/server/src/graph.rs(validate_plan_withfeeding the caller/stored partition — either side alone compiles and silently deletes the other PR's point) and #20 × #36 incrates/server/src/lib.rs(#20's wall-clock alignment withMINUTE_MSderived fromgate_core::COUNTERS_WINDOW_SECONDSrather than a fresh literal).What merging this accepts
Every behaviour change the audit marked nota, at once: #25/#56 route to the DLQ traffic that flows today; #26 no longer pre-creates the egress queue (early subscribers get a 404); #6 cancels sibling stages on a non-retryable error; #38 resets counters whose keys contain
:or%once; #59/#60/#61/#63 answer 502 during a store or history outage instead of a stale zero. If any of those needs its own window, the branch can be cut at a phase boundary — everything through phase 5 carries none of them.Not proven here: multi-replica behaviour and network latency (one machine, one replica), and the two premises the audit could not verify from this repository — #26's
configuresemantics and #27'skv_apply_v1atomicity.🤖 Generated with Claude Code