Skip to content

feat(twap-monitor): PollOutcome lifecycle dispatch (BLEU-830) - #7

Closed
brunota20 wants to merge 2 commits into
feat/twap-monitor-retry-bleu-829from
feat/twap-monitor-lifecycle-bleu-830
Closed

feat(twap-monitor): PollOutcome lifecycle dispatch (BLEU-830)#7
brunota20 wants to merge 2 commits into
feat/twap-monitor-retry-bleu-829from
feat/twap-monitor-lifecycle-bleu-830

Conversation

@brunota20

Copy link
Copy Markdown

Summary

After `poll_one`, the non-Ready arms now reach a typed lifecycle step:

Outcome WatchUpdate Effect
`TryNextBlock` `NoOp` re-poll next block
`TryOnBlock(n)` `SetNextBlock(n)` persist `next_block:{owner}:{params_hash}`
`TryAtEpoch(t)` `SetNextEpoch(t)` persist `next_epoch:{owner}:{params_hash}`
`DontTryAgain` `DropWatch` delete `watch:{owner}:{params_hash}` + best-effort delete of stale `next_block:` / `next_epoch:` gates

Decision is split out as the pure `outcome_to_update` returning a `WatchUpdate` enum; the impure `apply_watch_update` performs the local-store writes. That partition lets the four host-free tests assert the mapping exhaustively without wit-bindgen scaffolding.

`Ready` is mapped to `NoOp` as a safety net — `poll_all_watches` routes Ready to `submit_ready`, which owns the post-submit book-keeping. If a future refactor pipes Ready through the lifecycle path, the watch must NOT be erased.

Wire-format conventions (u64 LE, key shape `watch:{owner}:{params_hash}` + parallel `next_block:` / `next_epoch:`) stay the same as BLEU-827; no consumer changes required.

Stacks on #6 (BLEU-829 retry).

Linear: BLEU-830.

Test plan

  • `cargo test -p twap-monitor` — 34 host tests (3 BLEU-826 + 14 BLEU-827 + 7 BLEU-828 + 5 BLEU-829 + 5 BLEU-830: Ready / TryNextBlock / TryOnBlock / TryAtEpoch / DontTryAgain).
  • `cargo clippy --target wasm32-wasip2 -p twap-monitor -- -Dwarnings`.
  • `cargo clippy -p twap-monitor --tests -- -Dwarnings`.
  • `cargo build --target wasm32-wasip2 --release -p twap-monitor` — 300 KB .wasm.

After poll_one, the non-Ready arms now reach a typed lifecycle
step instead of dead-ending in the log:

- TryNextBlock  -> NoOp                — re-poll next block
- TryOnBlock(n) -> SetNextBlock(n)     -> persist next_block:{...}
- TryAtEpoch(t) -> SetNextEpoch(t)     -> persist next_epoch:{...}
- DontTryAgain  -> DropWatch           -> delete watch:{...} +
                                          best-effort delete of the
                                          stale next_block: /
                                          next_epoch: gates

The decision is split out as a pure `outcome_to_update` returning
a `WatchUpdate` enum, with the impure `apply_watch_update`
performing the local-store writes. That partition lets the four
host-free tests assert the mapping exhaustively without
wit-bindgen scaffolding.

`Ready` is deliberately mapped to `NoOp` here as a safety net —
poll_all_watches routes Ready to submit_ready, which owns the
post-submit book-keeping (submitted: marker + retry / drop). If
a future refactor accidentally pipes Ready through the lifecycle
path, the watch must NOT be erased.

Wire-format conventions (u64 LE bytes, key shape watch:{owner}:
{params_hash} and parallel next_block: / next_epoch:) stay the
same as BLEU-827; no consumer changes required.

Tests: 5 new (Ready, TryNextBlock, TryOnBlock, TryAtEpoch,
DontTryAgain). Total 34 host tests.

Linear: BLEU-830.
@linear-code

linear-code Bot commented Jun 15, 2026

Copy link
Copy Markdown

BLEU-830

@brunota20

Copy link
Copy Markdown
Author

Work landed via dev/m3-base advance (FF to 9e76602 = M3 epic + rust-idiomatic compliance propagation). The HEAD commit of this PR is now an ancestor of dev/m3-base. Closing as merged-by-ancestor.

@brunota20 brunota20 closed this Jun 24, 2026
brunota20 added a commit that referenced this pull request Jun 25, 2026
12 review threads addressed end-to-end. Net diff is -720 lines despite
adding ~200 lines of new helpers + tests, because the WitBindgenHost
adapter deduplication alone wipes ~400 lines.

Per-thread:

  #1 (balance-tracker architecture): refactored to match the M3
  host-trait+adapter split the other 4 modules use. Created
  `strategy.rs` with `on_block(&impl Host, ...)`, moved check_one /
  fetch_balance / parse_balance_hex / parse_settings into it,
  converted parse_config to use SDK config helpers + typed
  HostError instead of String. Added 3 MockHost-driven tests
  covering first-seen-above-threshold, below-threshold-persist,
  and error-does-not-abort-loop.

  #2 + #3 (WitBindgenHost dedup): new
  `shepherd_sdk::bind_host_via_wit_bindgen!()` declarative macro.
  Single source of truth in `crates/shepherd-sdk/src/wit_bindgen_macro.rs`;
  the 4 trait impls + convert_err / sdk_err_into_wit / convert_level
  collapse to one macro invocation per module. Migrated all 5
  modules (twap-monitor, ethflow-watcher, price-alert, stop-loss,
  balance-tracker). Each module's lib.rs lost ~80 lines.

  #4 (scale_decimal + config_get dup): new `shepherd_sdk::config`
  with `get_required`, `get_optional`, `scale_decimal`, and a
  typed `ConfigError` enum (host-neutral). price-alert + stop-loss
  consume the SDK helpers; their local duplicates were deleted.
  Module-level decimal-parsing tests removed (covered by 7 SDK
  tests + 4 proptest cases now).

  #5 (Chainlink dup): new `shepherd_sdk::chain::chainlink` with
  `read_latest_answer(host, chain_id, oracle, domain) -> Option<I256>`.
  Encapsulates the eth_call → parse → ABI decode flow + Warn
  logging. price-alert + stop-loss now call the helper; their
  local AggregatorV3 sol! definitions + read_oracle / on_block
  oracle plumbing was deleted. SDK ships with 3 StubHost tests
  covering happy path, host error, and garbage-hex.

  #6 (WIT world capability elision): added new "Capability
  enforcement vs. the WIT world" section to ADR-0009 documenting
  that price-alert + balance-tracker compile against the
  shepherd:cow/shepherd supertype but their manifests omit
  cow-api, and that boot success depends on wasm-tools' unused-
  import elision. Flagged as load-bearing; M5 macro hardening
  path documented.

  #7 (poll-time revert classification inert): filed COW-1082 for
  the host-side fix (forward structured eth_call error data into
  HostError.data; analogous to COW-1075 for orderbook).

  #8 (classify_api_error retry-default unbounded): filed COW-1083
  for the rate-limit / max-retry follow-up on the backoff: marker.

  #9 (RetryAction::Backoff dead variant): no code change; replied
  to thread clarifying it is reserved API surface waiting on a
  richer upstream retry_hint shape (open question for mfw78).

  #10 (no proptest anywhere): added `proptest` to shepherd-sdk
  dev-dependencies. New `crates/shepherd-sdk/src/proptests.rs`
  with 6 properties covering eth_call_params/parse_eth_call_result
  round-trip, parse_eth_call_result rejection on unquoted input,
  config::scale_decimal round-trip + sign-preservation, U256 LE
  byte round-trip, and no-panic guards for decode_revert_hex +
  gpv2_to_order_data marker dispatch.

  #11 (ethflow chain capability least-privilege): moved `chain`
  from required to optional in `modules/ethflow-watcher/module.toml`,
  mirroring the M2 mirror fix already applied.

  #12 (ADR-0009 test-count census): dropped the "145 host tests
  (twap 20, ethflow 12, ...)" breakdown; kept the qualitative
  claim. CI is now the authoritative count.

Drive-by: alloy-sol-types moved from regular to dev-dependencies in
price-alert and stop-loss now that the Chainlink ABI helper is
inside shepherd-sdk and the modules only use sol! in their test
helpers.

Validation:
- cargo test --workspace: every crate green; 5 modules + SDK + sdk-test + engine all pass. 8 host tests gained on balance-tracker; 6 proptest props gained on shepherd-sdk; 3 Chainlink helper tests gained.
- cargo clippy --workspace --all-targets -- -D warnings: clean.
- cargo fmt --check: clean.
- cargo build --target wasm32-wasip2 --release for all 5 modules: clean.
- Zero em-dashes in source code added.
brunota20 added a commit that referenced this pull request Jun 25, 2026
12 review threads addressed end-to-end. Net diff is -720 lines despite
adding ~200 lines of new helpers + tests, because the WitBindgenHost
adapter deduplication alone wipes ~400 lines.

Per-thread:

  #1 (balance-tracker architecture): refactored to match the M3
  host-trait+adapter split the other 4 modules use. Created
  `strategy.rs` with `on_block(&impl Host, ...)`, moved check_one /
  fetch_balance / parse_balance_hex / parse_settings into it,
  converted parse_config to use SDK config helpers + typed
  HostError instead of String. Added 3 MockHost-driven tests
  covering first-seen-above-threshold, below-threshold-persist,
  and error-does-not-abort-loop.

  #2 + #3 (WitBindgenHost dedup): new
  `shepherd_sdk::bind_host_via_wit_bindgen!()` declarative macro.
  Single source of truth in `crates/shepherd-sdk/src/wit_bindgen_macro.rs`;
  the 4 trait impls + convert_err / sdk_err_into_wit / convert_level
  collapse to one macro invocation per module. Migrated all 5
  modules (twap-monitor, ethflow-watcher, price-alert, stop-loss,
  balance-tracker). Each module's lib.rs lost ~80 lines.

  #4 (scale_decimal + config_get dup): new `shepherd_sdk::config`
  with `get_required`, `get_optional`, `scale_decimal`, and a
  typed `ConfigError` enum (host-neutral). price-alert + stop-loss
  consume the SDK helpers; their local duplicates were deleted.
  Module-level decimal-parsing tests removed (covered by 7 SDK
  tests + 4 proptest cases now).

  #5 (Chainlink dup): new `shepherd_sdk::chain::chainlink` with
  `read_latest_answer(host, chain_id, oracle, domain) -> Option<I256>`.
  Encapsulates the eth_call → parse → ABI decode flow + Warn
  logging. price-alert + stop-loss now call the helper; their
  local AggregatorV3 sol! definitions + read_oracle / on_block
  oracle plumbing was deleted. SDK ships with 3 StubHost tests
  covering happy path, host error, and garbage-hex.

  #6 (WIT world capability elision): added new "Capability
  enforcement vs. the WIT world" section to ADR-0009 documenting
  that price-alert + balance-tracker compile against the
  shepherd:cow/shepherd supertype but their manifests omit
  cow-api, and that boot success depends on wasm-tools' unused-
  import elision. Flagged as load-bearing; M5 macro hardening
  path documented.

  #7 (poll-time revert classification inert): filed COW-1082 for
  the host-side fix (forward structured eth_call error data into
  HostError.data; analogous to COW-1075 for orderbook).

  #8 (classify_api_error retry-default unbounded): filed COW-1083
  for the rate-limit / max-retry follow-up on the backoff: marker.

  #9 (RetryAction::Backoff dead variant): no code change; replied
  to thread clarifying it is reserved API surface waiting on a
  richer upstream retry_hint shape (open question for mfw78).

  #10 (no proptest anywhere): added `proptest` to shepherd-sdk
  dev-dependencies. New `crates/shepherd-sdk/src/proptests.rs`
  with 6 properties covering eth_call_params/parse_eth_call_result
  round-trip, parse_eth_call_result rejection on unquoted input,
  config::scale_decimal round-trip + sign-preservation, U256 LE
  byte round-trip, and no-panic guards for decode_revert_hex +
  gpv2_to_order_data marker dispatch.

  #11 (ethflow chain capability least-privilege): moved `chain`
  from required to optional in `modules/ethflow-watcher/module.toml`,
  mirroring the M2 mirror fix already applied.

  #12 (ADR-0009 test-count census): dropped the "145 host tests
  (twap 20, ethflow 12, ...)" breakdown; kept the qualitative
  claim. CI is now the authoritative count.

Drive-by: alloy-sol-types moved from regular to dev-dependencies in
price-alert and stop-loss now that the Chainlink ABI helper is
inside shepherd-sdk and the modules only use sol! in their test
helpers.

Validation:
- cargo test --workspace: every crate green; 5 modules + SDK + sdk-test + engine all pass. 8 host tests gained on balance-tracker; 6 proptest props gained on shepherd-sdk; 3 Chainlink helper tests gained.
- cargo clippy --workspace --all-targets -- -D warnings: clean.
- cargo fmt --check: clean.
- cargo build --target wasm32-wasip2 --release for all 5 modules: clean.
- Zero em-dashes in source code added.
brunota20 added a commit that referenced this pull request Jun 25, 2026
12 review threads addressed end-to-end. Net diff is -720 lines despite
adding ~200 lines of new helpers + tests, because the WitBindgenHost
adapter deduplication alone wipes ~400 lines.

Per-thread:

  #1 (balance-tracker architecture): refactored to match the M3
  host-trait+adapter split the other 4 modules use. Created
  `strategy.rs` with `on_block(&impl Host, ...)`, moved check_one /
  fetch_balance / parse_balance_hex / parse_settings into it,
  converted parse_config to use SDK config helpers + typed
  HostError instead of String. Added 3 MockHost-driven tests
  covering first-seen-above-threshold, below-threshold-persist,
  and error-does-not-abort-loop.

  #2 + #3 (WitBindgenHost dedup): new
  `shepherd_sdk::bind_host_via_wit_bindgen!()` declarative macro.
  Single source of truth in `crates/shepherd-sdk/src/wit_bindgen_macro.rs`;
  the 4 trait impls + convert_err / sdk_err_into_wit / convert_level
  collapse to one macro invocation per module. Migrated all 5
  modules (twap-monitor, ethflow-watcher, price-alert, stop-loss,
  balance-tracker). Each module's lib.rs lost ~80 lines.

  #4 (scale_decimal + config_get dup): new `shepherd_sdk::config`
  with `get_required`, `get_optional`, `scale_decimal`, and a
  typed `ConfigError` enum (host-neutral). price-alert + stop-loss
  consume the SDK helpers; their local duplicates were deleted.
  Module-level decimal-parsing tests removed (covered by 7 SDK
  tests + 4 proptest cases now).

  #5 (Chainlink dup): new `shepherd_sdk::chain::chainlink` with
  `read_latest_answer(host, chain_id, oracle, domain) -> Option<I256>`.
  Encapsulates the eth_call → parse → ABI decode flow + Warn
  logging. price-alert + stop-loss now call the helper; their
  local AggregatorV3 sol! definitions + read_oracle / on_block
  oracle plumbing was deleted. SDK ships with 3 StubHost tests
  covering happy path, host error, and garbage-hex.

  #6 (WIT world capability elision): added new "Capability
  enforcement vs. the WIT world" section to ADR-0009 documenting
  that price-alert + balance-tracker compile against the
  shepherd:cow/shepherd supertype but their manifests omit
  cow-api, and that boot success depends on wasm-tools' unused-
  import elision. Flagged as load-bearing; M5 macro hardening
  path documented.

  #7 (poll-time revert classification inert): filed COW-1082 for
  the host-side fix (forward structured eth_call error data into
  HostError.data; analogous to COW-1075 for orderbook).

  #8 (classify_api_error retry-default unbounded): filed COW-1083
  for the rate-limit / max-retry follow-up on the backoff: marker.

  #9 (RetryAction::Backoff dead variant): no code change; replied
  to thread clarifying it is reserved API surface waiting on a
  richer upstream retry_hint shape (open question for mfw78).

  #10 (no proptest anywhere): added `proptest` to shepherd-sdk
  dev-dependencies. New `crates/shepherd-sdk/src/proptests.rs`
  with 6 properties covering eth_call_params/parse_eth_call_result
  round-trip, parse_eth_call_result rejection on unquoted input,
  config::scale_decimal round-trip + sign-preservation, U256 LE
  byte round-trip, and no-panic guards for decode_revert_hex +
  gpv2_to_order_data marker dispatch.

  #11 (ethflow chain capability least-privilege): moved `chain`
  from required to optional in `modules/ethflow-watcher/module.toml`,
  mirroring the M2 mirror fix already applied.

  #12 (ADR-0009 test-count census): dropped the "145 host tests
  (twap 20, ethflow 12, ...)" breakdown; kept the qualitative
  claim. CI is now the authoritative count.

Drive-by: alloy-sol-types moved from regular to dev-dependencies in
price-alert and stop-loss now that the Chainlink ABI helper is
inside shepherd-sdk and the modules only use sol! in their test
helpers.

Validation:
- cargo test --workspace: every crate green; 5 modules + SDK + sdk-test + engine all pass. 8 host tests gained on balance-tracker; 6 proptest props gained on shepherd-sdk; 3 Chainlink helper tests gained.
- cargo clippy --workspace --all-targets -- -D warnings: clean.
- cargo fmt --check: clean.
- cargo build --target wasm32-wasip2 --release for all 5 modules: clean.
- Zero em-dashes in source code added.
brunota20 added a commit that referenced this pull request Jun 25, 2026
12 review threads addressed end-to-end. Net diff is -720 lines despite
adding ~200 lines of new helpers + tests, because the WitBindgenHost
adapter deduplication alone wipes ~400 lines.

Per-thread:

  #1 (balance-tracker architecture): refactored to match the M3
  host-trait+adapter split the other 4 modules use. Created
  `strategy.rs` with `on_block(&impl Host, ...)`, moved check_one /
  fetch_balance / parse_balance_hex / parse_settings into it,
  converted parse_config to use SDK config helpers + typed
  HostError instead of String. Added 3 MockHost-driven tests
  covering first-seen-above-threshold, below-threshold-persist,
  and error-does-not-abort-loop.

  #2 + #3 (WitBindgenHost dedup): new
  `shepherd_sdk::bind_host_via_wit_bindgen!()` declarative macro.
  Single source of truth in `crates/shepherd-sdk/src/wit_bindgen_macro.rs`;
  the 4 trait impls + convert_err / sdk_err_into_wit / convert_level
  collapse to one macro invocation per module. Migrated all 5
  modules (twap-monitor, ethflow-watcher, price-alert, stop-loss,
  balance-tracker). Each module's lib.rs lost ~80 lines.

  #4 (scale_decimal + config_get dup): new `shepherd_sdk::config`
  with `get_required`, `get_optional`, `scale_decimal`, and a
  typed `ConfigError` enum (host-neutral). price-alert + stop-loss
  consume the SDK helpers; their local duplicates were deleted.
  Module-level decimal-parsing tests removed (covered by 7 SDK
  tests + 4 proptest cases now).

  #5 (Chainlink dup): new `shepherd_sdk::chain::chainlink` with
  `read_latest_answer(host, chain_id, oracle, domain) -> Option<I256>`.
  Encapsulates the eth_call → parse → ABI decode flow + Warn
  logging. price-alert + stop-loss now call the helper; their
  local AggregatorV3 sol! definitions + read_oracle / on_block
  oracle plumbing was deleted. SDK ships with 3 StubHost tests
  covering happy path, host error, and garbage-hex.

  #6 (WIT world capability elision): added new "Capability
  enforcement vs. the WIT world" section to ADR-0009 documenting
  that price-alert + balance-tracker compile against the
  shepherd:cow/shepherd supertype but their manifests omit
  cow-api, and that boot success depends on wasm-tools' unused-
  import elision. Flagged as load-bearing; M5 macro hardening
  path documented.

  #7 (poll-time revert classification inert): filed COW-1082 for
  the host-side fix (forward structured eth_call error data into
  HostError.data; analogous to COW-1075 for orderbook).

  #8 (classify_api_error retry-default unbounded): filed COW-1083
  for the rate-limit / max-retry follow-up on the backoff: marker.

  #9 (RetryAction::Backoff dead variant): no code change; replied
  to thread clarifying it is reserved API surface waiting on a
  richer upstream retry_hint shape (open question for mfw78).

  #10 (no proptest anywhere): added `proptest` to shepherd-sdk
  dev-dependencies. New `crates/shepherd-sdk/src/proptests.rs`
  with 6 properties covering eth_call_params/parse_eth_call_result
  round-trip, parse_eth_call_result rejection on unquoted input,
  config::scale_decimal round-trip + sign-preservation, U256 LE
  byte round-trip, and no-panic guards for decode_revert_hex +
  gpv2_to_order_data marker dispatch.

  #11 (ethflow chain capability least-privilege): moved `chain`
  from required to optional in `modules/ethflow-watcher/module.toml`,
  mirroring the M2 mirror fix already applied.

  #12 (ADR-0009 test-count census): dropped the "145 host tests
  (twap 20, ethflow 12, ...)" breakdown; kept the qualitative
  claim. CI is now the authoritative count.

Drive-by: alloy-sol-types moved from regular to dev-dependencies in
price-alert and stop-loss now that the Chainlink ABI helper is
inside shepherd-sdk and the modules only use sol! in their test
helpers.

Validation:
- cargo test --workspace: every crate green; 5 modules + SDK + sdk-test + engine all pass. 8 host tests gained on balance-tracker; 6 proptest props gained on shepherd-sdk; 3 Chainlink helper tests gained.
- cargo clippy --workspace --all-targets -- -D warnings: clean.
- cargo fmt --check: clean.
- cargo build --target wasm32-wasip2 --release for all 5 modules: clean.
- Zero em-dashes in source code added.
brunota20 added a commit that referenced this pull request Jun 25, 2026
…tParseError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #7
(`Result<_, String>` survivor in `modules/examples/balance-tracker/
src/strategy.rs:149`).

The rubric prohibits stringly-typed errors in library APIs. The
formatting strings now live on `#[error(...)]` annotations on the
`AddressListParseError` variants, preserving the exact wording the
previous `format!("address #{i} ({trimmed:?}): {e}")` /
`"expected at least one address"` calls produced, so any
operator-facing log strings stay stable.

`thiserror = "2"` lands as a direct dep on the balance-tracker
module. The audit also notes shepherd-backtest already migrated to a
typed `AddressParseError`; consolidating both into a shared
`shepherd_sdk::cow::AddressParse` is a separate refactor flagged as a
P3 medium-confidence consolidation in the audit and deferred for
Bruno's judgment call on whether the shared crate is the right home.
brunota20 added a commit that referenced this pull request Jun 25, 2026
12 review threads addressed end-to-end. Net diff is -720 lines despite
adding ~200 lines of new helpers + tests, because the WitBindgenHost
adapter deduplication alone wipes ~400 lines.

Per-thread:

  #1 (balance-tracker architecture): refactored to match the M3
  host-trait+adapter split the other 4 modules use. Created
  `strategy.rs` with `on_block(&impl Host, ...)`, moved check_one /
  fetch_balance / parse_balance_hex / parse_settings into it,
  converted parse_config to use SDK config helpers + typed
  HostError instead of String. Added 3 MockHost-driven tests
  covering first-seen-above-threshold, below-threshold-persist,
  and error-does-not-abort-loop.

  #2 + #3 (WitBindgenHost dedup): new
  `shepherd_sdk::bind_host_via_wit_bindgen!()` declarative macro.
  Single source of truth in `crates/shepherd-sdk/src/wit_bindgen_macro.rs`;
  the 4 trait impls + convert_err / sdk_err_into_wit / convert_level
  collapse to one macro invocation per module. Migrated all 5
  modules (twap-monitor, ethflow-watcher, price-alert, stop-loss,
  balance-tracker). Each module's lib.rs lost ~80 lines.

  #4 (scale_decimal + config_get dup): new `shepherd_sdk::config`
  with `get_required`, `get_optional`, `scale_decimal`, and a
  typed `ConfigError` enum (host-neutral). price-alert + stop-loss
  consume the SDK helpers; their local duplicates were deleted.
  Module-level decimal-parsing tests removed (covered by 7 SDK
  tests + 4 proptest cases now).

  #5 (Chainlink dup): new `shepherd_sdk::chain::chainlink` with
  `read_latest_answer(host, chain_id, oracle, domain) -> Option<I256>`.
  Encapsulates the eth_call → parse → ABI decode flow + Warn
  logging. price-alert + stop-loss now call the helper; their
  local AggregatorV3 sol! definitions + read_oracle / on_block
  oracle plumbing was deleted. SDK ships with 3 StubHost tests
  covering happy path, host error, and garbage-hex.

  #6 (WIT world capability elision): added new "Capability
  enforcement vs. the WIT world" section to ADR-0009 documenting
  that price-alert + balance-tracker compile against the
  shepherd:cow/shepherd supertype but their manifests omit
  cow-api, and that boot success depends on wasm-tools' unused-
  import elision. Flagged as load-bearing; M5 macro hardening
  path documented.

  #7 (poll-time revert classification inert): filed COW-1082 for
  the host-side fix (forward structured eth_call error data into
  HostError.data; analogous to COW-1075 for orderbook).

  #8 (classify_api_error retry-default unbounded): filed COW-1083
  for the rate-limit / max-retry follow-up on the backoff: marker.

  #9 (RetryAction::Backoff dead variant): no code change; replied
  to thread clarifying it is reserved API surface waiting on a
  richer upstream retry_hint shape (open question for mfw78).

  #10 (no proptest anywhere): added `proptest` to shepherd-sdk
  dev-dependencies. New `crates/shepherd-sdk/src/proptests.rs`
  with 6 properties covering eth_call_params/parse_eth_call_result
  round-trip, parse_eth_call_result rejection on unquoted input,
  config::scale_decimal round-trip + sign-preservation, U256 LE
  byte round-trip, and no-panic guards for decode_revert_hex +
  gpv2_to_order_data marker dispatch.

  #11 (ethflow chain capability least-privilege): moved `chain`
  from required to optional in `modules/ethflow-watcher/module.toml`,
  mirroring the M2 mirror fix already applied.

  #12 (ADR-0009 test-count census): dropped the "145 host tests
  (twap 20, ethflow 12, ...)" breakdown; kept the qualitative
  claim. CI is now the authoritative count.

Drive-by: alloy-sol-types moved from regular to dev-dependencies in
price-alert and stop-loss now that the Chainlink ABI helper is
inside shepherd-sdk and the modules only use sol! in their test
helpers.

Validation:
- cargo test --workspace: every crate green; 5 modules + SDK + sdk-test + engine all pass. 8 host tests gained on balance-tracker; 6 proptest props gained on shepherd-sdk; 3 Chainlink helper tests gained.
- cargo clippy --workspace --all-targets -- -D warnings: clean.
- cargo fmt --check: clean.
- cargo build --target wasm32-wasip2 --release for all 5 modules: clean.
- Zero em-dashes in source code added.
brunota20 added a commit that referenced this pull request Jun 25, 2026
…tParseError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #7
(`Result<_, String>` survivor in `modules/examples/balance-tracker/
src/strategy.rs:149`).

The rubric prohibits stringly-typed errors in library APIs. The
formatting strings now live on `#[error(...)]` annotations on the
`AddressListParseError` variants, preserving the exact wording the
previous `format!("address #{i} ({trimmed:?}): {e}")` /
`"expected at least one address"` calls produced, so any
operator-facing log strings stay stable.

`thiserror = "2"` lands as a direct dep on the balance-tracker
module. The audit also notes shepherd-backtest already migrated to a
typed `AddressParseError`; consolidating both into a shared
`shepherd_sdk::cow::AddressParse` is a separate refactor flagged as a
P3 medium-confidence consolidation in the audit and deferred for
Bruno's judgment call on whether the shared crate is the right home.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant