cow: run the cow keeper on the generic venue client#468
Conversation
lgahdl
left a comment
There was a problem hiding this comment.
The CowApiTransport bridge itself is solid — submit fully implemented (including the already-submitted case), quote/status/cancel are explicit tested Unsupported stubs (not silent no-ops), fault projection preserves retry-hint granularity (RateLimited with its ms hint, Timeout, HTTP 429 vs. other, routed through the already-ratified #464 classifier) rather than flattening, retry_action's publicization is signature-only with no behavior change, and the "legacy path never touched" test genuinely asserts a zero call-count on a spy mock rather than just omitting a negative assertion.
Two things carry over unresolved from #466 (confirmed still live at this point in the stack, not reintroduced by this PR): the journal's contains/record check-then-act pair still sandwiches the network submit with no atomicity (same race), and the journal key still hashes the full signed body including signature bytes (a re-signed identical order still defeats dedup rather than triggering it) — this PR only swaps what runs between the guard, not the guard itself. Worth tracking those against #466 rather than as new findings here.
Two new things worth a look, flagged here in the body since one line isn't near a diff hunk:
-
crates/shepherd-sdk/src/cow/mod.rs:32—classify_submit_errorstays in this public re-export list, but its only in-crate caller (run.rs's oldsubmit_ready) was deleted by this PR — the live path now calls a different, newly-introducedclassify_api_errorintransport.rsinstead. So this is now an orphaned public export: nothing in this crate exercises it anymore, but an external consumer could still import it, and since it's a completely separate function from the one that actually runs, its logic can silently drift from the live classification path with no compiler signal. Worth removing the stale re-export in this same PR, or folding it intoclassify_api_errorif it's meant to still be equivalent. -
crates/shepherd-sdk/src/cow/transport.rs:527(CowApiError::Fault(fault) => VenueFault::Unavailable(fault.to_string())) — any host-levelFault, includingFault::Denied(e.g. a misconfigured capability allowlist, a genuinely permanent condition), projects toVenueFault::Unavailable, whichretry_actiontreats as retryable. The PR's own test (host_faults_stay_retryable_and_never_drop_the_watch) confirms this is intentional, but it means a misconfigured allowlist retries every tick forever with only log noise, rather than surfacing distinctly from a transient infrastructure hiccup. Not a new regression (carried-over design choice), but worth a bounded retry count or distinct alerting forFault::Deniedspecifically.
What
Flips the CoW keeper's submit path from
CowApiHost::submit_orderonto the typedvidere_sdk::VenueClient(CowClient<T>).run/submit_readynow take a&CowClient<T>and dispatch throughVenueTransport::submit, keying the idempotency journal on the genericsubmission_key(venue, body). A newCowApiTransportbridges the typed client onto the still-live legacyshepherd:cow/cow-apihost until module worlds flip tovidere:venue/client; it is the only caller left ofCowApiHost::submit_order.videre_sdk::keeper::retry_actionis made public and takes&VenueFaultsoruncan fold venue refusals intoRetryActionoutsideKeeper::sweep.Why
M4's go/no-go gate: the flagship CoW keeper must submit through the generic venue seam, not bypass it. This is the Rust-side seam port only; the legacy host extension stays wired until the module worlds flip, and the fork-gated orderbook poll wire-swap is untouched.
Testing
crates/shepherd-sdk/tests/run.rs: existing suite ported ontorun(&host, &venue(&host), ...); newready_submits_the_encoded_intent_body_through_the_venue_seamspies the rawVenueTransportto prove the wire carries the encodedCowIntentBodyunderCowVenue::IDand the legacy cow-api path is never touched.crates/shepherd-sdk-test/tests/mock_venue.rs: ported onto the typed client.crates/shepherd-sdk/src/cow/transport.rs: unit tests onCowApiTransport's fault projection and receipt decoding.AI Assistance
Implemented with Claude Code assistance; reviewed and directed by mfw78.
Closes #400