From 4d8c713b3c33c7051b2b9e177760017e8bc6acc3 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sat, 17 Oct 2026 23:15:42 +0700 Subject: [PATCH] spec(queen): vendor dispatch_exit -- a provider refusal never charges the issue (t27#10008) Re-vendors gHashTag/t27 specs/queen/dispatch_exit.t27 from t27 PR #10011 (blob 4f9fa86b), byte-identical, with dispatch_exit.wasm rebuilt by the PIN recipe (zig 0.16.0, wasm32-wasi, reactor, every fn exported). The recipe rebuilds the old card to the pinned 31c0e524 and the new card to b990a601 on two builds. counts_against_issue(DX_PROVIDER_ERROR) is now false, so the bee actors stop giving an issue up after three quota refusals; the next dispatch takes another key. A bad request still gives up at once, and a repeating refusal still ends at STREAK_LIMIT. No TypeScript changes: the hosts read the card from the wasm. Co-Authored-By: Claude Opus 5.5 --- trios/agent-server/specs/PIN | 6 ++-- .../specs/queen/dispatch_exit.t27 | 26 ++++++++++++++++-- .../specs/queen/dispatch_exit.wasm | Bin 820 -> 827 bytes 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/trios/agent-server/specs/PIN b/trios/agent-server/specs/PIN index 3df032bcea..66d75ba92b 100644 --- a/trios/agent-server/specs/PIN +++ b/trios/agent-server/specs/PIN @@ -38,9 +38,9 @@ turn_stop: queen/turn_stop.wasm sha256 264c027d05cbdebd8ef48c259f1eb64e006240c7f keyed card: gHashTag/t27 specs/queen/keyed.t27 (t27#8271, PR t27#8286, blob af81c437a03fe1e9b1a41a965e76b7b06222b551), byte-identical; keyed.wasm = t27c gen-c + zig 0.16.0 cc --target=wasm32-wasi -Oz -DNDEBUG -mexec-model=reactor, exports every fn of the card: key_action,evictable,passivate_due,evict_before,entry_after_exit,hold_admits,restart_after_exit,lane_free,claim_step,keeps_lane,claim_is_mine,keeps_lane_after_end,retry_after_lost_seconds,bee_key_next,bee_key_acts,key_holds_work,starts_in (reproducible: two builds, one hash) keyed: queen/keyed.t27 sha256 2c396c47a4b27d995c79e8a1b91ad87d3772463aa9a6d7794e78aed30a4c8b3e keyed: queen/keyed.wasm sha256 90c4fb9b5594dff336110ebeb0a8fc54048002de833cc69529551cb8dbda8610 -dispatch_exit card: gHashTag/t27 specs/queen/dispatch_exit.t27 (t27#7494, PR t27#8043, blob 36cbd440f7959fa2e43543928e10f9c98db3f33b, master d2b5fc129), byte-identical; dispatch_exit.wasm = t27c gen-c + zig 0.16.0 cc --target=wasm32-wasi -Oz -DNDEBUG -mexec-model=reactor, exports every fn of the card: http_class,provider_failed,runtime_alive,past_timeout,exit_reason,actors_reason,counts_against_issue,restart_decision,restart_wait_seconds (reproducible: two builds, one hash) -dispatch_exit: queen/dispatch_exit.t27 sha256 ac1f481c62b16c3783ffeb83c682422a0c0fd0498169d8c101db160768913a3b -dispatch_exit: queen/dispatch_exit.wasm sha256 31c0e5248c9a12da2a67af4c1e5e34648111818fdb345df2c509f148d7d5b02a +dispatch_exit card: gHashTag/t27 specs/queen/dispatch_exit.t27 (t27#7494, PR t27#8043; a provider refusal never charges the issue: t27#10008, PR t27#10011, blob 4f9fa86b1e680405edc03bea1f3eea23ce4a1612), byte-identical; dispatch_exit.wasm = t27c gen-c + zig 0.16.0 cc --target=wasm32-wasi -Oz -DNDEBUG -mexec-model=reactor, exports every fn of the card: http_class,provider_failed,runtime_alive,past_timeout,exit_reason,actors_reason,counts_against_issue,restart_decision,restart_wait_seconds (reproducible: two builds, one hash) +dispatch_exit: queen/dispatch_exit.t27 sha256 2a1b5a1bb24298ee05319cd9458d0a31c4e596a4d87fe9e360fdc93dc9a56037 +dispatch_exit: queen/dispatch_exit.wasm sha256 b990a601e4c1806095688683e3c25f0b5176124d368dfd258fdcdd7934c56327 netlink card: gHashTag/t27 specs/queen/netlink.t27 (t27#8276, PR t27#8278, blob 429db9dff5669599dd4234285e95c2cef2babd3f, master 2ae4fc9c2), byte-identical; netlink.wasm = t27c gen-c + zig 0.16.0 cc --target=wasm32-wasi -Oz -DNDEBUG -mexec-model=reactor, exports every fn of the card: lease_up,next_incarnation,inc_next_gen,inc_of,write_admitted,beat_renews,must_fence,mail_action,forget_handled,peer_change,peer_up,lost_with,mail_expired (reproducible: two builds, one hash) netlink: queen/netlink.t27 sha256 447de25019d810d96b164cfda863d4b4708eb567e6635933cafa9f41c980724a netlink: queen/netlink.wasm sha256 015891d2bc9395d781c690f2611a3f545067c62ff2b79d0ade628ef280e3fd1b diff --git a/trios/agent-server/specs/queen/dispatch_exit.t27 b/trios/agent-server/specs/queen/dispatch_exit.t27 index 36cbd440f7..4f9fa86b1e 100644 --- a/trios/agent-server/specs/queen/dispatch_exit.t27 +++ b/trios/agent-server/specs/queen/dispatch_exit.t27 @@ -25,6 +25,9 @@ ; once its lease was taken from it. What is left -- a silent runtime that still holds its lease past ; one TTL without a heartbeat -- contradicts the lease rule itself (a lease expires one TTL after the ; last renewal), so UNKNOWN names an observation that is inconsistent, not a cause nobody looked for. +; #10008 (2026-10-17): a provider error no longer counts against the issue. A quota or an overload +; belongs to the key that served the turn, the next try takes another key, and key_failover.t27 +; charges_issue charges only a bad request; this card now agrees with it. ; Not here: the wiring. The host is TypeScript in gHashTag/BrowserOS (queen-dispatch.ts); the test ; `conformance_rows` is one assert per row, inputs to code, so the host can run the same rows. ASCII ; only (L3). @@ -145,11 +148,16 @@ pub const BACKOFF_CAP : u32 = 300; /// Whether this end is a counted run of the issue. An agent runtime is a transient child (actors.t27 /// queen_restart_type): normal and shutdown end it for good and count nothing, a cancel is an /// interruption (control.t27 cancel_counts_against_issue). A dropped connection is an interruption -/// too: the runtime was alive and the work did nothing wrong. Every other end is a failed run. +/// too: the runtime was alive and the work did nothing wrong. A provider error is the provider's or +/// the key's, not the issue's (#10008): the next try takes another key, and key_failover.t27 +/// charges_issue charges only a bad request. A bad request still gives up at once, by its own rule in +/// restart_decision, and a refusal that repeats still ends at STREAK_LIMIT. Every other end is a +/// failed run. pub fn counts_against_issue(reason: u8) -> bool { if (reason == DX_NORMAL) { return false; } if (reason == DX_SHUTDOWN) { return false; } if (reason == DX_NOCONNECTION) { return false; } + if (reason == DX_PROVIDER_ERROR) { return false; } return true; } @@ -267,7 +275,7 @@ test the_third_counted_run_gives_up_and_an_interruption_is_not_counted { assert counts_against_issue(DX_NOCONNECTION) == false; assert counts_against_issue(DX_KILLED) == true; assert counts_against_issue(DX_TIMEOUT) == true; - assert counts_against_issue(DX_PROVIDER_ERROR) == true; + assert counts_against_issue(DX_PROVIDER_ERROR) == false; assert counts_against_issue(DX_UNKNOWN) == true; assert restart_decision(DX_TIMEOUT, HC_NONE, 1, 1) == RD_LATER; assert restart_decision(DX_TIMEOUT, HC_NONE, 2, 2) == RD_GIVE_UP; @@ -279,6 +287,20 @@ test the_third_counted_run_gives_up_and_an_interruption_is_not_counted { assert restart_decision(DX_SHUTDOWN, HC_NONE, 9, 9) == RD_NONE; } +/// #10008: a quota, an overload or a 5xx is the key's, not the issue's. Past RUN_LIMIT counted runs +/// it still waits and tries again; a bad request still gives up at once; a refusal that repeats ends +/// at STREAK_LIMIT like any interruption. +test a_provider_refusal_never_charges_the_issue { + assert restart_decision(DX_PROVIDER_ERROR, HC_NONE, RUN_LIMIT - 1, RUN_LIMIT - 1) == RD_LATER; + assert restart_decision(DX_PROVIDER_ERROR, HC_429, RUN_LIMIT, RUN_LIMIT) == RD_LATER; + assert restart_decision(DX_PROVIDER_ERROR, HC_5XX, 9, STREAK_LIMIT - 1) == RD_LATER; + assert restart_decision(DX_PROVIDER_ERROR, HC_NONE, 0, STREAK_LIMIT) == RD_GIVE_UP; + assert restart_decision(DX_PROVIDER_ERROR, HC_4XX, 0, 0) == RD_GIVE_UP; + assert restart_decision(DX_PROVIDER_ERROR, HC_4XX, RUN_LIMIT, 1) == RD_GIVE_UP; + assert restart_decision(DX_TIMEOUT, HC_NONE, RUN_LIMIT - 1, RUN_LIMIT - 1) == RD_GIVE_UP; + assert restart_decision(DX_UNKNOWN, HC_429, RUN_LIMIT - 1, 0) == RD_GIVE_UP; +} + test an_interruption_that_repeats_also_ends { assert restart_decision(DX_NOCONNECTION, HC_NONE, 0, STREAK_LIMIT - 1) == RD_LATER; assert restart_decision(DX_NOCONNECTION, HC_NONE, 0, STREAK_LIMIT) == RD_GIVE_UP; diff --git a/trios/agent-server/specs/queen/dispatch_exit.wasm b/trios/agent-server/specs/queen/dispatch_exit.wasm index 19d9e606349bc347d88fe8d9f267536b5cb003bd..d4c8476fa8f97c99881529323d6d2db4fd439c92 100755 GIT binary patch delta 90 zcmdnOwwrB(5F_LA&BBbnjKYEp3Ji`M-i~`16&T8j92pC_lO~rj$?G#YFgXBas#`hcJ$aeH;>_;6433Qa+#t>Fyo}tFcQI7~0AoTCga7~l delta 109 zcmdnZwuNnj5F_J)&BBbnj8P&C3Ji{H-i|gv>OVtaS&<`SA$J5LV?C1tP=v{!m%)*N zpWBgHkp(Eg?8xfP%K(;PbK_-DU~;U>a;){_WdsW`y8{(7@^b_AaR8Z<4>DB&0IVt# A=>Px#