"""HUX-05 approval queue and the pre-side-effect gate. Security obligations exercised: SO-35 (only a human surface with router or relay trust decides; ``decision.by`` is the asserted user), SO-36 (``once`` is consumed exactly once), SO-37 (the gate must present the argument hash recorded at request time), SO-39 (external side effects always need an approval record), SO-40 (an exhausted budget blocks new approvals), SO-42 (pending approvals expire 24 h after ``requested_at``). """ from __future__ import annotations import importlib import json import sys from datetime import datetime, timedelta, timezone from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] FOUNDATION = ROOT / "dockerfiles" / "hermes-hux-foundation" if str(FOUNDATION) not in sys.path: sys.path.insert(0, str(FOUNDATION)) import hux # noqa: E402 from hux import audit, budgets, contracts, identity, policy, store # noqa: E402 from hux import events as hux_events # noqa: E402 from hux.server import build_router # noqa: E402 SCHEMAS = contracts.load_all() HEADERS = {"X-Hermes-Tenant-Identity": "slot-3", "X-Hux-Subject": "usr_0123456789abcdef", "X-Hux-Surface": "chat", "X-Hux-Relay-Key": "rk"} OTHER = {**HEADERS, "X-Hux-Subject": "usr_fedcba9876543210"} WORKER = {**HEADERS, "X-Hux-Surface": "worker", "X-Hux-Trust": "worker", "X-Hux-Relay-Key": "wk"} RELAY = {**HEADERS, "X-Hux-Surface": "telegram", "X-Hux-Trust": "relay", "X-Hux-Relay-Key": "rk"} ALL_ON = ",".join(card["flag"] for card in contracts.load_flags()["cards"]) T0 = datetime(2026, 8, 24, 12, 0, tzinfo=timezone.utc) CONV = "conv_0001abcd" HASH = "sha256:" + "ab" * 32 OTHER_HASH = "sha256:" + "cd" * 32 @pytest.fixture def router(tmp_path): instance = build_router(tmp_path, {"HUX_FLAGS": ALL_ON, "HUX_ROUTER_KEY": "rk", "HUX_WORKER_KEY": "wk", "HUX_RELAY_KEY": "rk"}) response = instance.dispatch( "POST", "/hux/v1/runs/run_9f/budget", WORKER, json.dumps({"conversation_id": CONV}).encode() ) assert response.status == 200 return instance @pytest.fixture def frozen(monkeypatch): state = {"now": T0} monkeypatch.setattr(policy, "clock", lambda: state["now"]) return state @pytest.fixture def events(monkeypatch): """Stand-in for the events lane; records every emit call.""" seen: list[dict] = [] monkeypatch.setattr(hux_events, "emit", lambda store, identity, conversation_id, kind, summary, **extra: seen.append({"conversation_id": conversation_id, "kind": kind, "summary": summary, **extra})) return seen def call(router, method, path, body=None, headers=HEADERS): raw = b"" if body is None else json.dumps(body).encode() response = router.dispatch(method, path, headers, raw) return response.status, response.body def valid(record): problems = contracts.validate_record(record, SCHEMAS) assert problems == [], problems return record def request_body(capability="write_files", external=False, run_id="run_9f", **extra): evidence = [{"kind": "tool_call", "id": "call-7", "hash": HASH}] return {"run_id": run_id, "conversation_id": CONV, "capability": capability, "request": {"summary": f"do {capability}", "risk": "low", "external": external, "evidence": evidence, **extra}} def set_autonomy(router, level): status, body = call(router, "PUT", "/hux/v1/policy", {"scope": {"level": "global"}, "autonomy": level}) assert status == 200, body # --- creation resolves against the matrix ------------------------------------------ def test_safe_policy_auto_allows_reads_and_queues_mutations(router, frozen, events): status, body = call(router, "POST", "/hux/v1/approvals", request_body("read_files"), WORKER) assert status == 201 and valid(body)["status"] == "approved" assert body["decision"] == {"choice": "once", "by": {"type": "system", "id": "policy"}, "at": policy.iso(T0)} status, body = call(router, "POST", "/hux/v1/approvals", request_body("write_files"), WORKER) assert status == 201 and valid(body)["status"] == "pending" and "decision" not in body assert body["expires_at"] == policy.iso(T0 + timedelta(hours=24)) status, body = call(router, "POST", "/hux/v1/approvals", request_body("network"), WORKER) assert status == 201 and valid(body)["status"] == "pending" and "decision" not in body, "safe parks network visibly" status, body = call(router, "POST", "/hux/v1/approvals", request_body("web_search"), WORKER) assert status == 201 and valid(body)["status"] == "pending", "safe parks web_search visibly" assert [e["kind"] for e in events] == ["approval.resolved", "approval.requested", "approval.requested", "approval.requested"] assert events[0]["evidence"] == [{"kind": "approval", "id": events[0]["evidence"][0]["id"]}] def test_external_side_effects_always_need_a_human_regardless_of_autonomy(router, events): set_autonomy(router, "autonomous") status, body = call(router, "POST", "/hux/v1/approvals", request_body("send_message"), WORKER) assert status == 201 and body["status"] == "approved" status, body = call(router, "POST", "/hux/v1/approvals", request_body("send_message", external=True), WORKER) assert status == 201 and body["status"] == "pending" for always_ask in ("deploy", "external_side_effect"): status, body = call(router, "POST", "/hux/v1/approvals", request_body(always_ask), WORKER) assert body["status"] == "pending", always_ask call(router, "PUT", "/hux/v1/policy", {"scope": {"level": "global"}, "autonomy": "autonomous", "grants": [{"capability": "network", "decision": "deny"}]}) status, body = call(router, "POST", "/hux/v1/approvals", request_body("network", external=True), WORKER) assert body["status"] == "denied", "deny beats external ask" def test_idempotency_key_returns_the_original(router): key = {"Idempotency-Key": "run_9f:approval:call-7"} status, first = call(router, "POST", "/hux/v1/approvals", request_body(), {**WORKER, **key}) assert status == 201 and first["idempotency_key"] == "run_9f:approval:call-7" status, again = call(router, "POST", "/hux/v1/approvals", request_body(), {**WORKER, **key}) assert (status, again) == (200, first) status, conflict = call(router, "POST", "/hux/v1/approvals", request_body("shell"), {**WORKER, **key}) assert (status, conflict["code"]) == (409, "conflict") status, other = call(router, "POST", "/hux/v1/approvals", request_body(), {**WORKER, "Idempotency-Key": "run_9f:approval:call-8"}) assert status == 201 and other["id"] != first["id"] assert call(router, "GET", f"/hux/v1/approvals/{first['id']}")[1] == first @pytest.mark.parametrize("body,status", [ (None, 400), ([], 400), ({"conversation_id": "nope", "capability": "shell"}, 400), ({"conversation_id": CONV, "capability": "teleport"}, 400), ({"conversation_id": CONV, "capability": "shell", "run_id": "r"}, 400), ({"conversation_id": CONV, "capability": "shell", "run_id": "r", "request": {"summary": "", "risk": "low"}}, 400), ({"conversation_id": CONV, "capability": "shell", "run_id": "r", "request": {"summary": "x", "risk": "silly"}}, 400), ]) def test_create_rejects_bad_bodies(router, body, status): got, error = call(router, "POST", "/hux/v1/approvals", body, WORKER) assert got == status and valid(error)["code"] == "invalid" # --- deciding ----------------------------------------------------------------------- def pending(router, capability="write_files", **kw): body = request_body(capability, **kw) run_id = body["run_id"] if budgets.run_conversation(router_store(router), run_id) is None: assert call(router, "POST", f"/hux/v1/runs/{run_id}/budget", {"conversation_id": body["conversation_id"]}, WORKER)[0] == 200 status, body = call(router, "POST", "/hux/v1/approvals", body, WORKER) assert status == 201 and body["status"] == "pending", body return body def test_only_humans_on_router_or_relay_decide(router, tmp_path): record = pending(router) status, error = call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}, WORKER) assert (status, error["code"]) == (403, "forbidden") api = {**HEADERS, "X-Hux-Surface": "api"} assert call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}, api)[0] == 403 status, body = call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}, RELAY) assert status == 200 and valid(body)["status"] == "approved" assert body["decision"]["by"] == {"type": "user", "id": HEADERS["X-Hux-Subject"]} rows = audit.recent(store.TenantStore(tmp_path, identity.resolve(HEADERS, {"HUX_ROUTER_KEY": "rk"}))) assert [(r["action"], r["outcome"]) for r in rows if r["action"] == "approvals.decide"] == [("approvals.decide", "deny")] * 2 + [("approvals.decide", "allow")] def test_terminal_states_never_change_again(router, events): record = pending(router) path = f"/hux/v1/approvals/{record['id']}" assert call(router, "POST", path, {"choice": "maybe"})[0] == 400 assert call(router, "POST", path, [])[0] == 400 status, body = call(router, "POST", path, {"choice": "deny"}) assert status == 200 and body["status"] == "denied" and body["decision"]["choice"] == "deny" status, error = call(router, "POST", path, {"choice": "once"}) assert (status, error["code"]) == (409, "conflict") assert call(router, "GET", path)[1] == body assert events[-1]["kind"] == "approval.resolved" and "denied" in events[-1]["summary"] def test_pending_approvals_expire_after_24h(router, frozen): record = pending(router) frozen["now"] = T0 + timedelta(hours=24) status, body = call(router, "GET", f"/hux/v1/approvals/{record['id']}") assert status == 200 and valid(body)["status"] == "expired" assert call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"})[0] == 409 assert call(router, "GET", "/hux/v1/approvals?status=expired")[1]["items"] == [body] assert call(router, "GET", "/hux/v1/approvals?status=pending")[1]["items"] == [] def test_session_and_always_create_grants_at_their_scope(router, frozen): record = pending(router, "shell") status, body = call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "session"}) assert status == 200 and body["decision"]["choice"] == "session" conv_policy = call(router, "GET", f"/hux/v1/policy?scope=conversation&scope_id={CONV}")[1] assert valid(conv_policy)["scope"] == {"level": "conversation", "scope_id": CONV} assert conv_policy["grants"] == [{"capability": "shell", "decision": "allow", "expires_at": policy.iso(T0 + policy.SESSION_TTL), "granted_by": {"type": "user", "id": HEADERS["X-Hux-Subject"]}}] status, body = call(router, "POST", "/hux/v1/approvals", request_body("shell"), WORKER) assert body["status"] == "approved", "later shell requests in this conversation auto-approve" assert call(router, "POST", "/hux/v1/approvals", request_body("write_files"), WORKER)[1]["status"] == "pending" record = pending(router, "write_files") call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "always"}) global_policy = call(router, "GET", "/hux/v1/policy")[1] assert [g["capability"] for g in global_policy["grants"]] == ["write_files"] assert global_policy["grants"][0]["expires_at"] == policy.iso(T0 + policy.ALWAYS_TTL) frozen["now"] = T0 + timedelta(hours=25) record = pending(router, "shell", run_id="run_other") call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "session"}) conv_policy = call(router, "GET", f"/hux/v1/policy?scope=conversation&scope_id={CONV}")[1] assert len(conv_policy["grants"]) == 1 and conv_policy["revision"] == 2, "a repeat grant replaces, never duplicates" def test_list_filters_and_orders_by_request_time(router, frozen): first = pending(router, "shell") frozen["now"] = T0 + timedelta(seconds=5) second = pending(router, "write_files") status, body = call(router, "GET", "/hux/v1/approvals") assert status == 200 and [i["id"] for i in body["items"]] == [first["id"], second["id"]] and body["next"] is None assert call(router, "GET", "/hux/v1/approvals?status=pending")[1]["items"] == body["items"] assert call(router, "GET", "/hux/v1/approvals?status=approved")[1]["items"] == [] assert call(router, "GET", "/hux/v1/approvals?status=bogus")[0] == 400 for item in body["items"]: valid(item) def test_second_subject_cannot_see_or_decide(router): record = pending(router) assert call(router, "GET", f"/hux/v1/approvals/{record['id']}", headers=OTHER)[0] == 404 assert call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}, OTHER)[0] == 404 assert call(router, "GET", "/hux/v1/approvals", headers=OTHER)[1]["items"] == [] assert call(router, "GET", "/hux/v1/approvals/apr_doesnotexist")[0] == 404 assert call(router, "GET", "/hux/v1/approvals/..")[0] == 400 # --- budgets block new approvals ------------------------------------------------------- def test_exhausted_budget_blocks_new_approvals(router, events): call(router, "PUT", "/hux/v1/policy", {"scope": {"level": "global"}, "autonomy": "safe", "budgets": {"tool_calls_per_run": 2}}) status, body = call(router, "POST", "/hux/v1/runs/run_9f/budget", {"conversation_id": CONV, "tool_calls": 2}, WORKER) assert status == 200 and valid(body)["exhausted"] == ["tool_calls_per_run"] status, error = call(router, "POST", "/hux/v1/approvals", request_body("read_files"), WORKER) assert (status, error["code"]) == (429, "budget_exhausted") and error["details"] == ["tool_calls_per_run"] assert [e["kind"] for e in events] == ["budget.exhausted", "budget.exhausted"] status, body = call(router, "POST", "/hux/v1/approvals", request_body("read_files", run_id="run_fresh"), WORKER) assert (status, body["code"]) == (400, "invalid"), "an unbound run cannot create an approval record" call(router, "POST", "/hux/v1/runs/run_fresh/budget", {"conversation_id": CONV}, WORKER) status, body = call(router, "POST", "/hux/v1/approvals", request_body("read_files", run_id="run_fresh"), WORKER) assert (status, body["code"]) == (429, "budget_exhausted"), "run rotation cannot reset conversation spend" # --- gate -------------------------------------------------------------------------- def gate(router, run_id="run_9f", capability="write_files", argument_hash=HASH, external=False, **extra): return call(router, "POST", f"/hux/v1/runs/{run_id}/gate", {"capability": capability, "argument_hash": argument_hash, "external": external, **extra}, WORKER) def test_gate_blocks_before_approval_and_releases_once_exactly_once(router, events, tmp_path): status, body = gate(router) assert status == 200 and body == {"proceed": False, "reason": "no approval for this run and capability"} record = pending(router) assert gate(router)[1]["proceed"] is False and "pending" in gate(router)[1]["reason"] call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}) status, wrong = gate(router, argument_hash=OTHER_HASH) assert wrong["proceed"] is False and "different arguments" in wrong["reason"], "TOCTOU (SO-37)" status, body = gate(router) assert body == {"proceed": True, "approval_id": record["id"], "reason": "released"} status, again = gate(router) assert again["proceed"] is False and "already consumed" in again["reason"], "once is once (SO-36)" kinds = [e["kind"] for e in events] assert kinds.count("side_effect.released") == 1 and kinds.count("side_effect.blocked") == 5 released = next(e for e in events if e["kind"] == "side_effect.released") assert released["evidence"] == [{"kind": "approval", "id": record["id"]}] and released["run_id"] == "run_9f" served = call(router, "GET", f"/hux/v1/approvals/{record['id']}")[1] assert valid(served) and "_consumed_at" not in served rows = audit.recent(store.TenantStore(tmp_path, identity.resolve(HEADERS, {"HUX_ROUTER_KEY": "rk"}))) assert [r["outcome"] for r in rows if r["action"] == "gate.check"] == ["deny", "deny", "deny", "deny", "allow", "deny"] def test_gate_session_is_reusable_within_the_conversation(router): record = pending(router, "shell") call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "session"}) assert gate(router, capability="shell")[1]["proceed"] is True assert gate(router, capability="shell", argument_hash=OTHER_HASH)[1]["proceed"] is True assert gate(router, run_id="run_later", capability="shell")[1]["proceed"] is False, "unknown runs inherit no approval" status, unbound = call(router, "POST", "/hux/v1/approvals", request_body("shell", run_id="run_unbound"), WORKER) assert (status, unbound["code"]) == (400, "invalid"), "an unknown run cannot mint any approval record" call(router, "POST", "/hux/v1/runs/run_later/budget", {"conversation_id": CONV}, WORKER) assert gate(router, run_id="run_later", capability="shell")[1]["proceed"] is False, "the old approval record never spans runs" status, fresh = call(router, "POST", "/hux/v1/approvals", request_body("shell", run_id="run_later"), WORKER) assert status == 201 and fresh["status"] == "approved" and fresh["id"] != record["id"] assert gate(router, run_id="run_later", capability="shell")[1]["approval_id"] == fresh["id"] call(router, "POST", "/hux/v1/runs/run_elsewhere/budget", {"conversation_id": "conv_elsewhere01", "tokens": 1}, WORKER) status, body = gate(router, run_id="run_elsewhere", capability="shell", conversation_id=CONV) assert body["proceed"] is False, "the run's own conversation wins over the body's claim (F4)" elsewhere = request_body("shell", run_id="run_elsewhere") elsewhere["conversation_id"] = "conv_elsewhere01" status, scoped = call(router, "POST", "/hux/v1/approvals", elsewhere, WORKER) assert status == 201 and scoped["status"] == "pending", "a session grant never crosses conversations" assert gate(router, capability="write_files")[1]["proceed"] is False def test_gate_requires_external_approvals_for_external_effects_and_respects_expiry(router, frozen): record = pending(router, "send_message") call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}) status, body = gate(router, capability="send_message", external=True) assert body["proceed"] is False and "not requested as external" in body["reason"] record = pending(router, "send_message", external=True) call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}) frozen["now"] = T0 + timedelta(hours=25) status, body = gate(router, capability="send_message", external=True) assert body["proceed"] is False and "expired" in body["reason"] def test_gate_rejects_bad_bodies_and_other_tenants(router): assert gate(router, capability="teleport")[0] == 400 assert gate(router, argument_hash="md5:zz")[0] == 400 assert gate(router, conversation_id="nope")[0] == 200, "the body's conversation_id is ignored, never validated (F4)" assert call(router, "POST", "/hux/v1/runs/run_9f/gate", [], WORKER)[0] == 400 assert call(router, "POST", f"/hux/v1/runs/{'r' * 121}/gate", {"capability": "shell", "argument_hash": HASH}, WORKER)[0] == 400 record = pending(router) call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "once"}) status, body = call(router, "POST", "/hux/v1/runs/run_9f/gate", {"capability": "write_files", "argument_hash": HASH}, OTHER) assert (status, body["code"]) == (403, "forbidden") def test_events_module_absence_is_tolerated(router, monkeypatch): monkeypatch.setitem(sys.modules, "hux.events", None) monkeypatch.delattr(hux, "events") with pytest.raises(ImportError): importlib.import_module("hux.events") assert call(router, "POST", "/hux/v1/approvals", request_body("read_files"), WORKER)[0] == 201 policy.emit(None, None, None, "x", "no conversation means no event") assert budgets.hashes_of({"request": {}}) == set() # --- F4 / F6: external effects, run conversation and budgets at the gate -------------- def test_external_effects_release_only_for_the_same_run_and_arguments(router): """F4 (high) / SO-39: a session or always approval for an external effect never releases another run or other arguments.""" record = pending(router, "send_message", external=True) call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "session"}) assert gate(router, capability="send_message", external=True)[1]["proceed"] is True assert gate(router, capability="send_message", external=True)[1]["proceed"] is True, "session is reusable for the same run and hash" status, body = gate(router, capability="send_message", external=True, argument_hash=OTHER_HASH) assert body["proceed"] is False and "different arguments" in body["reason"] status, body = gate(router, run_id="run_z", capability="send_message", external=True, conversation_id=CONV) assert body["proceed"] is False and body["reason"] == "no approval for this run and capability" assert gate(router, run_id="run_z", capability="send_message", external=False)[1]["proceed"] is False, "an external approval is not a general one" always = pending(router, "send_message", external=True, run_id="run_y") call(router, "POST", f"/hux/v1/approvals/{always['id']}", {"choice": "always"}) assert gate(router, run_id="run_y", capability="send_message", external=True)[1]["proceed"] is True assert gate(router, run_id="run_w", capability="send_message", external=True)[1]["proceed"] is False, "always never spans runs for external effects" def test_once_approvals_name_exactly_one_tool_call(router): """F4 / SO-37: a request carrying two tool_call hashes is refused so a once approval designates one hash.""" body = request_body("shell") body["request"]["evidence"] = [{"kind": "tool_call", "id": "a", "hash": HASH}, {"kind": "tool_call", "id": "b", "hash": OTHER_HASH}] status, error = call(router, "POST", "/hux/v1/approvals", body, WORKER) assert (status, error["code"]) == (400, "invalid") and "exactly one tool_call" in error["message"] body["request"]["evidence"] = [{"kind": "tool_call", "id": "a", "hash": HASH}, {"kind": "file", "id": "notes.md"}] assert call(router, "POST", "/hux/v1/approvals", body, WORKER)[0] == 201, "other evidence kinds do not count" body["request"]["evidence"] = "not-a-list" assert call(router, "POST", "/hux/v1/approvals", body, WORKER)[0] == 400, "the contract still rejects it, without a 500" def test_gate_refuses_when_the_run_budget_is_exhausted(router, events): """F6 (medium) / SO-40: an approved effect still does not proceed once any limit is spent.""" record = pending(router, "shell") call(router, "POST", f"/hux/v1/approvals/{record['id']}", {"choice": "session"}) assert gate(router, capability="shell")[1]["proceed"] is True call(router, "PUT", "/hux/v1/policy", {"scope": {"level": "conversation", "scope_id": CONV}, "autonomy": "safe", "budgets": {"tool_calls_per_run": 2}}) call(router, "POST", "/hux/v1/runs/run_9f/budget", {"conversation_id": CONV, "tool_calls": 2}, WORKER) status, body = gate(router, capability="shell") assert body == {"proceed": False, "reason": "budget_exhausted", "exhausted": ["tool_calls_per_run"]} assert events[-1]["kind"] == "budget.exhausted" and events[-1]["run_id"] == "run_9f" and events[-1]["conversation_id"] == CONV assert gate(router, run_id="run_fresh", capability="shell")[1]["proceed"] is False, "unknown runs inherit nothing" call(router, "POST", "/hux/v1/runs/run_fresh/budget", {"conversation_id": CONV}, WORKER) assert gate(router, run_id="run_fresh", capability="shell")[1]["reason"] == "budget_exhausted" status, error = call(router, "POST", "/hux/v1/approvals", request_body("shell", run_id="run_fresh"), WORKER) assert (status, error["code"]) == (429, "budget_exhausted") call(router, "POST", "/hux/v1/runs/run_other/budget", {"conversation_id": "conv_other0001"}, WORKER) assert gate(router, run_id="run_other", capability="shell")[1]["proceed"] is False assert budgets.run_conversation(router_store(router), "run_nobody") is None def test_conversation_budget_aggregates_across_runs_and_policy_revision_resets(router): """Run rotation cannot reset spend; an explicit human policy revision starts a new budget epoch.""" policy_body = { "scope": {"level": "conversation", "scope_id": CONV}, "autonomy": "safe", "budgets": {"tool_calls_per_run": 3}, } assert call(router, "PUT", "/hux/v1/policy", policy_body)[0] == 200 first = call(router, "POST", "/hux/v1/runs/run_a/budget", {"conversation_id": CONV, "tool_calls": 2}, WORKER)[1] second = call(router, "POST", "/hux/v1/runs/run_b/budget", {"conversation_id": CONV, "tool_calls": 1}, WORKER)[1] assert first["spent"]["tool_calls"] == 2 assert second["spent"]["tool_calls"] == 3 and second["exhausted"] == ["tool_calls_per_run"] assert gate(router, run_id="run_b", capability="shell")[1]["reason"] == "budget_exhausted" revised = call(router, "PUT", "/hux/v1/policy", policy_body)[1] assert revised["revision"] == 2 reset = call(router, "GET", "/hux/v1/runs/run_b/budget", headers=WORKER)[1] assert reset["spent"]["tool_calls"] == 0 and reset["exhausted"] == [] restarted = call(router, "POST", "/hux/v1/runs/run_b/budget", {"conversation_id": CONV, "tool_calls": 1}, WORKER)[1] assert restarted["spent"]["tool_calls"] == 1, "the old epoch's per-run spend does not leak into the new epoch" def router_store(router): return store.TenantStore(router.data_root, identity.resolve(HEADERS, {"HUX_ROUTER_KEY": "rk"}))