Task 1 decision (proceed-docs-with-probe-first, operator-selected via
AskUserQuestion 2026-08-05): 13-ROUTSTR-FINDINGS.md observed 0 of 9 cited
protocol claims (no live provider was announcing on any of the 3 default
relays in a 30s window on 2026-08-03; relay reachability itself WAS
confirmed). This backend is written against docs.routstr.com's cited shape,
with the first live chat-completions call doubling as the capability probe:
a non-success HTTP status or a response missing the expected
choices[0].message shape fails loudly (bails with the real status/body)
rather than silently degrading.
- assistant/backends/routstr.rs (new): RoutstrBackend implements the
Backend trait — discover_providers subscribes for kind-38421
provider-announcement events over the existing Tor-proxy-aware Nostr
client (nostr_discovery::build_nostr_client, never a second relay
client), process-cached with a 5-minute TTL; select_provider picks the
globally cheapest affordable (provider, model) price across every
discovered provider (Routstr has no fixed target model the way
Ollama/Claude do — CONTEXT.md delegates provider selection strategy to
Claude's discretion), preferring an onion endpoint when Tor is up;
attach_payment calls the existing budget-capped auto_pay_token verbatim
(never hand-rolled); parse_openai_tool_calls parses the one
string-encoded function.arguments shape exactly once at this adapter's
edge; screen_outbound (G-B1/G-B2) runs before any body leaves the node,
exactly as it does for Claude; ROUTSTR_MAX_TOKENS caps every request
explicitly.
- assistant/egress.rs: message_is_turn_own gains "system" and "tool" role
handling plus an OpenAI tool_calls-sibling-field check — the pre-existing
function was written only against Claude's wire shape (system as a
top-level field, tool results wrapped in role:"user") and would have
silently stripped Routstr's system prompt and tool-result context out of
every outbound request via G-B2's fail-closed default arm. Fixed with 4
new regression tests pinning both wire shapes.
- assistant/backends/mod.rs: registers `pub mod routstr;`. select_backend's
actual wiring of the Routstr leg (budget-gated, per D-05) is Task 3's
commit, once AssistantBudget exists — this task's own acceptance criteria
do not require select_backend integration, only the adapter itself.
30/30 assistant::backends:: tests pass (17 new in routstr.rs, 3 new in
egress.rs's OpenAI-shape regression tests were run separately at 12/12).
Zero new packages (nostr-sdk/reqwest already in-tree); dispatcher.rs and
Cargo.toml untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>