Skip to content

docs: ADR bundle 0001-0007 — engine and CoW architectural decisions#1

Closed
brunota20 wants to merge 3 commits into
mainfrom
docs/adr-bundle
Closed

docs: ADR bundle 0001-0007 — engine and CoW architectural decisions#1
brunota20 wants to merge 3 commits into
mainfrom
docs/adr-bundle

Conversation

@brunota20

@brunota20 brunota20 commented Jun 2, 2026

Copy link
Copy Markdown

Captures the eight architectural decisions Bleu has made (engine-side) or proposed (CoW host helpers, upstream policy, forward-looking factory pattern) across the M1 → M2 work. All eight carry status proposed pending mfw78 sign-off; landing them here makes the decisions reviewable independently from the implementing draft PRs in nullislabs/shepherd (#8, #9, #10).

ADRs are ordered for reading (foundations → CoW infra → CoW helpers → cross-cutting), not chronologically.

Scope

ADR Title Implementing PR
0001 Operator engine.toml separate from module nexum.toml nullislabs#8, #9
0002 Per-chain alloy provider transport selected by URL scheme nullislabs#8, #9
0003 Per-module namespacing in local-store via length-prefixed key nullislabs#8
0004 Patch cowprotocol to bleu/cow-rs main nullislabs#10
0005 cow-api host backend via cached OrderBookApi per chain nullislabs#8
0006 TWAP and EthFlow as intent helpers in shepherd:cow@0.2.0 (design — not yet implemented)
0007 Push CoW Protocol logic to cow-rs first, adopt in engine second policy
0008 Dynamic address registration for log subscriptions (Envio-style) (design — forward-looking, M3+)

Also adds data/ (engine state_dir default) and skills-lock.json (local skill lockfile) to .gitignore so the engine's runtime state does not pollute the working tree.

Format

ADRs follow the grill-with-docs format: short title, 1–2 paragraph Context, Decision, Considered options (rejected alternatives worth remembering), and Consequences. Frontmatter carries status: proposed and an implemented-in: <PR> pointer where applicable.

Highlights worth mfw78's attention

  • ADR-0003 diverges from docs/04-state-store.md — the linked doc specifies one redb file per module; we propose a single shared file with a [len:u8][module][key] prefix. Rationale in the ADR. This is the one ADR where Bleu deliberately deviates from the published design and needs explicit sign-off.
  • ADR-0006 is the M2 grant deliverable design — TWAP and EthFlow as thin host helpers backed by upstream cowprotocol primitives. The EthFlow module is positioned as the Rust externalization of the ethflow indexer currently inside cowprotocol/services/crates/autopilot/src/database/onchain_order_events/ethflow_events.rs, in line with how watch-tower and refunder were extracted.
  • ADR-0007 is the load-bearing policy — five protocol primitives (composable::poll_and_build_order with rich PollOutcome, eth_flow::decode_placement, OrderPostError rich variants + retry_hint, OrderBookApi::with_base_url, wasm32 feature-gate) land in bleu/cow-rs first and are consumed via the patched dependency in ADR-0004.
  • ADR-0008 is forward-looking — does not affect M2 deliverables (TWAP and EthFlow use static subscriptions only). Lands in the 0.2 breaking-change window so adding factory support later does not require a major bump.

Next

Once mfw78 reacts, statuses flip to accepted (or superseded) in a follow-up commit. The most likely point of pushback is ADR-0003 (single redb file vs per-module file); the rest of the surface is either gap-filling or implementation choice over interfaces mfw78 already specified.

Comment thread docs/adr/0007-upstream-protocol-logic-to-cow-rs.md Outdated
Comment thread docs/adr/0008-factory-subscriptions-in-manifest.md Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that here we have another decision to make. If we need to make historical index as well. Let's discuss this with mfw but it makes the project more complex without need

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When sending this to mfw, I will comment that this last ADR is discussable

Comment thread docs/adr/0006-cow-twap-ethflow-host-helpers.md Outdated
Comment thread docs/adr/0006-cow-twap-ethflow-host-helpers.md Outdated
Comment thread docs/adr/0005-cow-api-via-cached-orderbookapi.md
dependabot Bot added 2 commits June 14, 2026 03:59
Updates the requirements on [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen) to permit the latest version.
- [Release notes](https://github.com/bytecodealliance/wit-bindgen/releases)
- [Commits](bytecodealliance/wit-bindgen@v0.57.0...v0.58.0)

---
updated-dependencies:
- dependency-name: wit-bindgen
  dependency-version: 0.58.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@de0fac2...df4cb1c)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Replaces the original docs/adr-bundle commit chain with the current accurate
version of the same file set. Each ADR + diagram now matches the design as
shipped through M5:

- ADR-0001 nexum.toml -> module.toml rename
- ADR-0003 32-byte hash namespacing in redb local-store
- ADR-0006 capability gating is link-time (boot validation against
  KNOWN_CAPABILITIES), not per-call dispatch
- diagrams.md ASCII hyphens per rubric; vapor features (cargo-nexum CLI,
  nexum-sdk separate crate, :8080/health endpoint, [module.resources]
  per-module caps) marked as future direction (0.3+ target), with pointers
  to ADR-0009 for the host-trait seam that supersedes the macro design.

Same file set as before (8 ADRs in docs/adr/, 9 diagrams in docs/diagrams/,
the original CI workflow + .gitignore + modules/example/Cargo.toml deltas).
This is a force-push that replaces the original PR #12 head; mfw78's prior
review threads will need to be re-anchored against the updated text.

AI Assistance: Claude Code (Opus 4.7) rebuilt the branch by copying file
content from chore/docs-reconciliation-m2-m5 (M5-current + reconciliation
applied) onto a fresh tree off the upstream main base. A human (Bruno) is
accountable for the result.
@brunota20

Copy link
Copy Markdown
Author

Bleu-fork mirror of upstream nullislabs/shepherd#12. The canonical ADR bundle delivery happens upstream (now at head 439bd2f, reconciled). Closing this bleu-side vestige since bleu:main will catch up when upstream merges.

@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
The supervisor's dispatch path is per-chain by construction
(`dispatch_block(block)` filters modules by `block.chain_id`
matching their `[[subscription]]` table), and the COW-1071 WS
reconnect tasks own one per-chain backoff timer each. Multi-chain
isolation is therefore structural, not derived. This PR locks the
guarantee into the test suite with two new integration tests + a
supervisor.rs docstring stating the invariant explicitly.

## New tests

`multi_chain_dispatch_isolates_modules_by_chain`:
- Boot two `example` modules with different `[[subscription]]`
  chain_ids (1 + 100).
- Dispatch a block on chain 1 -> only module-a receives it
  (dispatched=1, alive_count=2 unchanged).
- Dispatch a block on chain 100 -> only module-b receives it.
- Validates: subscription filter is per-chain; a block on one
  chain does not even enter modules subscribed to a different
  chain.

`multi_chain_poisoned_module_does_not_affect_other_chains`:
- Boot fuel-bomb (always-traps) on chain 1 + example (healthy)
  on chain 100, with `PoisonPolicy::new(2, 60s)`.
- Trap bomb #1 on chain 1 -> bomb dies, poisoned=0, example
  untouched.
- Dispatch on chain 100 -> example receives (1/1).
- Wait 1.1 s (bomb backoff window), trap bomb #2 -> poisoned=1.
- Dispatch on chain 100 again -> example STILL receives.
- Validates: a permanently-poisoned module on one chain does not
  consume restart slots, fuel, or scheduling attention from
  modules on any other chain.

Total wall-clock ~1.2 s for the second test (one backoff window).

## supervisor.rs docstring

The module-level comment now articulates the multi-chain isolation
invariant explicitly so a future reader of the dispatch path knows
the property is load-bearing.

## What this proves

Supervisor side (dispatch fast-path):
- Per-module `alive`, `failure_count`, `next_attempt`, `poisoned`
  are independent of which chain triggered the event.
- Subscription filter excludes mismatched modules before any
  dispatch / restart logic runs.

Upstream side (already proven by COW-1071's architecture):
- `open_block_streams` spawns one task per chain; tasks share no
  state. A chain-A WS drop changes only chain-A's task state.
- `open_log_streams` is per-(module, chain) -> even tighter
  isolation than block streams.

## Out of scope

- A unit test that "fakes a WS drop" on chain A while chain B
  keeps yielding. Requires mocking `ProviderPool::subscribe_blocks`
  which today goes through real alloy / tokio infrastructure. The
  COW-1064 (E2E 4-6h testnet) and COW-1031 (7-day soak) will
  exercise this path against live RPCs.
- Per-chain configurable backoff / health-window. Today the
  reconnect policy is workspace-wide; per-chain tuning is a 0.3
  follow-up.

## Workspace impact

- `cargo test --workspace` -> 163 host tests + 6 doctests passing
  (was 161 + 6; +2 from the new integration tests).
- `cargo clippy --all-targets --workspace -- -D warnings` clean.
- `cargo fmt --all --check` clean.

Linear: COW-1073. Ninth M4 issue landed; stacks on #42 (COW-1072).
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
The supervisor's dispatch path is per-chain by construction
(`dispatch_block(block)` filters modules by `block.chain_id`
matching their `[[subscription]]` table), and the COW-1071 WS
reconnect tasks own one per-chain backoff timer each. Multi-chain
isolation is therefore structural, not derived. This PR locks the
guarantee into the test suite with two new integration tests + a
supervisor.rs docstring stating the invariant explicitly.

## New tests

`multi_chain_dispatch_isolates_modules_by_chain`:
- Boot two `example` modules with different `[[subscription]]`
  chain_ids (1 + 100).
- Dispatch a block on chain 1 -> only module-a receives it
  (dispatched=1, alive_count=2 unchanged).
- Dispatch a block on chain 100 -> only module-b receives it.
- Validates: subscription filter is per-chain; a block on one
  chain does not even enter modules subscribed to a different
  chain.

`multi_chain_poisoned_module_does_not_affect_other_chains`:
- Boot fuel-bomb (always-traps) on chain 1 + example (healthy)
  on chain 100, with `PoisonPolicy::new(2, 60s)`.
- Trap bomb #1 on chain 1 -> bomb dies, poisoned=0, example
  untouched.
- Dispatch on chain 100 -> example receives (1/1).
- Wait 1.1 s (bomb backoff window), trap bomb #2 -> poisoned=1.
- Dispatch on chain 100 again -> example STILL receives.
- Validates: a permanently-poisoned module on one chain does not
  consume restart slots, fuel, or scheduling attention from
  modules on any other chain.

Total wall-clock ~1.2 s for the second test (one backoff window).

## supervisor.rs docstring

The module-level comment now articulates the multi-chain isolation
invariant explicitly so a future reader of the dispatch path knows
the property is load-bearing.

## What this proves

Supervisor side (dispatch fast-path):
- Per-module `alive`, `failure_count`, `next_attempt`, `poisoned`
  are independent of which chain triggered the event.
- Subscription filter excludes mismatched modules before any
  dispatch / restart logic runs.

Upstream side (already proven by COW-1071's architecture):
- `open_block_streams` spawns one task per chain; tasks share no
  state. A chain-A WS drop changes only chain-A's task state.
- `open_log_streams` is per-(module, chain) -> even tighter
  isolation than block streams.

## Out of scope

- A unit test that "fakes a WS drop" on chain A while chain B
  keeps yielding. Requires mocking `ProviderPool::subscribe_blocks`
  which today goes through real alloy / tokio infrastructure. The
  COW-1064 (E2E 4-6h testnet) and COW-1031 (7-day soak) will
  exercise this path against live RPCs.
- Per-chain configurable backoff / health-window. Today the
  reconnect policy is workspace-wide; per-chain tuning is a 0.3
  follow-up.

## Workspace impact

- `cargo test --workspace` -> 163 host tests + 6 doctests passing
  (was 161 + 6; +2 from the new integration tests).
- `cargo clippy --all-targets --workspace -- -D warnings` clean.
- `cargo fmt --all --check` clean.

Linear: COW-1073. Ninth M4 issue landed; stacks on #42 (COW-1072).
brunota20 added a commit that referenced this pull request Jun 25, 2026
Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1.
Vendored rubric mandates `strum::IntoStaticStr` (with
`#[strum(serialize_all = "snake_case")]`) on every error enum so
`error_kind` labels on the `shepherd_chain_request_total` /
`shepherd_cow_api_*` counters stay in lock-step with the Rust source
of truth instead of growing a `match err { ... => "connect" ... }`
ladder per call site.

Enums covered on this milestone (the ones present on dev/m2-base):
- `nexum_engine::host::cow_orderbook::CowApiError`
- `nexum_engine::host::provider_pool::ProviderError`
- `nexum_engine::manifest::error::ParseError`
- `nexum_engine::engine_config::EngineConfigError`

Also adds `#[non_exhaustive]` to `CowApiError` and `ProviderError`
(audit Major #2). The other two already carried it.

`strum = "0.26"` lands as a direct dep on nexum-engine. The
workspace-deps hoist (audit P1, Major #5) is intentionally a separate
judgment call left to Bruno; this commit ships the substantive rubric
fix without coupling to the broader Cargo.toml restructure.
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
…host/cow enums

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
and Major #2.

The rubric mandates `strum::IntoStaticStr` on every error / event
enum (snake_case variant names land as `&'static str` for metric
labels and structured-log `error_kind` fields) and
`#[non_exhaustive]` on any public enum that may grow variants.

Two enums on the SDK side:
- `shepherd_sdk::host::HostErrorKind`: adds both attributes (previously
  carried neither; M5 audit table is wrong - the M5 tip lacked
  non_exhaustive on m3-base specifically because the m4 cherry-pick
  added it. Landing on m3-base instead so the SDK ships with it).
- `shepherd_sdk::cow::error::RetryAction`: already had non_exhaustive;
  derive adds the IntoStaticStr.

The `bind_host_via_wit_bindgen!` macro gains a `_ => Internal`
wildcard in its SDK -> wit-bindgen HostErrorKind remap so module
crates compile against future variants without source changes (the
same change M5 already carries; landing here on the SDK home
milestone keeps the SDK self-contained).

`strum = "0.26"` is a `default-features = false` SDK dep.
brunota20 added a commit that referenced this pull request Jun 25, 2026
…BuildError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
and Major #2 (BuildError listed for both treatments).

`BuildError` is the cowprotocol-side rejection envelope returned when
`build_order_creation` cannot assemble an `OrderCreation` body. The
submission-failure warn log can now carry
`error_kind = unknown_marker | cowprotocol` directly off the enum
instead of growing a `match err { ... => "unknown_marker" ... }`
ladder in the call site.

`strum = "0.26"` (default-features = false) lands as a direct dep of
the twap-monitor module. The enum is `enum BuildError` (not `pub`)
but adding `non_exhaustive` is still the rubric default for error
enums: it documents intent and costs nothing at the single call site
inside this module.
brunota20 added a commit that referenced this pull request Jun 25, 2026
The supervisor's dispatch path is per-chain by construction
(`dispatch_block(block)` filters modules by `block.chain_id`
matching their `[[subscription]]` table), and the COW-1071 WS
reconnect tasks own one per-chain backoff timer each. Multi-chain
isolation is therefore structural, not derived. This PR locks the
guarantee into the test suite with two new integration tests + a
supervisor.rs docstring stating the invariant explicitly.

## New tests

`multi_chain_dispatch_isolates_modules_by_chain`:
- Boot two `example` modules with different `[[subscription]]`
  chain_ids (1 + 100).
- Dispatch a block on chain 1 -> only module-a receives it
  (dispatched=1, alive_count=2 unchanged).
- Dispatch a block on chain 100 -> only module-b receives it.
- Validates: subscription filter is per-chain; a block on one
  chain does not even enter modules subscribed to a different
  chain.

`multi_chain_poisoned_module_does_not_affect_other_chains`:
- Boot fuel-bomb (always-traps) on chain 1 + example (healthy)
  on chain 100, with `PoisonPolicy::new(2, 60s)`.
- Trap bomb #1 on chain 1 -> bomb dies, poisoned=0, example
  untouched.
- Dispatch on chain 100 -> example receives (1/1).
- Wait 1.1 s (bomb backoff window), trap bomb #2 -> poisoned=1.
- Dispatch on chain 100 again -> example STILL receives.
- Validates: a permanently-poisoned module on one chain does not
  consume restart slots, fuel, or scheduling attention from
  modules on any other chain.

Total wall-clock ~1.2 s for the second test (one backoff window).

## supervisor.rs docstring

The module-level comment now articulates the multi-chain isolation
invariant explicitly so a future reader of the dispatch path knows
the property is load-bearing.

## What this proves

Supervisor side (dispatch fast-path):
- Per-module `alive`, `failure_count`, `next_attempt`, `poisoned`
  are independent of which chain triggered the event.
- Subscription filter excludes mismatched modules before any
  dispatch / restart logic runs.

Upstream side (already proven by COW-1071's architecture):
- `open_block_streams` spawns one task per chain; tasks share no
  state. A chain-A WS drop changes only chain-A's task state.
- `open_log_streams` is per-(module, chain) -> even tighter
  isolation than block streams.

## Out of scope

- A unit test that "fakes a WS drop" on chain A while chain B
  keeps yielding. Requires mocking `ProviderPool::subscribe_blocks`
  which today goes through real alloy / tokio infrastructure. The
  COW-1064 (E2E 4-6h testnet) and COW-1031 (7-day soak) will
  exercise this path against live RPCs.
- Per-chain configurable backoff / health-window. Today the
  reconnect policy is workspace-wide; per-chain tuning is a 0.3
  follow-up.

## Workspace impact

- `cargo test --workspace` -> 163 host tests + 6 doctests passing
  (was 161 + 6; +2 from the new integration tests).
- `cargo clippy --all-targets --workspace -- -D warnings` clean.
- `cargo fmt --all --check` clean.

Linear: COW-1073. Ninth M4 issue landed; stacks on #42 (COW-1072).
brunota20 added a commit that referenced this pull request Jun 25, 2026
…terError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
(remaining enums introduced on the M5 multi-chain pass).

- `EnvVarError` (engine_config.rs): introduced with the COW-1071 env-var
  substitution path. Snake_case variant labels feed the boot-time
  `tracing::error!(error_kind = ...)` call sites in `main.rs`.
- `FilterError` (supervisor.rs): introduced with the M5 multi-chain
  log-filter parsing. Snake_case variant labels feed the
  `tracing::warn!(error_kind = ...)` log emitted when a
  `[[subscription]]` address or topic fails to parse.

The audit's M3 / M4 derives landed on the milestones that introduced
the enums; these two complete the workspace-wide IntoStaticStr pass
flagged in audit Major #1 on the milestones that own them.
brunota20 added a commit that referenced this pull request Jun 25, 2026
Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1.
Vendored rubric mandates `strum::IntoStaticStr` (with
`#[strum(serialize_all = "snake_case")]`) on every error enum so
`error_kind` labels on the `shepherd_chain_request_total` /
`shepherd_cow_api_*` counters stay in lock-step with the Rust source
of truth instead of growing a `match err { ... => "connect" ... }`
ladder per call site.

Enums covered on this milestone (the ones present on dev/m2-base):
- `nexum_engine::host::cow_orderbook::CowApiError`
- `nexum_engine::host::provider_pool::ProviderError`
- `nexum_engine::manifest::error::ParseError`
- `nexum_engine::engine_config::EngineConfigError`

Also adds `#[non_exhaustive]` to `CowApiError` and `ProviderError`
(audit Major #2). The other two already carried it.

`strum = "0.26"` lands as a direct dep on nexum-engine. The
workspace-deps hoist (audit P1, Major #5) is intentionally a separate
judgment call left to Bruno; this commit ships the substantive rubric
fix without coupling to the broader Cargo.toml restructure.
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
…host/cow enums

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
and Major #2.

The rubric mandates `strum::IntoStaticStr` on every error / event
enum (snake_case variant names land as `&'static str` for metric
labels and structured-log `error_kind` fields) and
`#[non_exhaustive]` on any public enum that may grow variants.

Two enums on the SDK side:
- `shepherd_sdk::host::HostErrorKind`: adds both attributes (previously
  carried neither; M5 audit table is wrong - the M5 tip lacked
  non_exhaustive on m3-base specifically because the m4 cherry-pick
  added it. Landing on m3-base instead so the SDK ships with it).
- `shepherd_sdk::cow::error::RetryAction`: already had non_exhaustive;
  derive adds the IntoStaticStr.

The `bind_host_via_wit_bindgen!` macro gains a `_ => Internal`
wildcard in its SDK -> wit-bindgen HostErrorKind remap so module
crates compile against future variants without source changes (the
same change M5 already carries; landing here on the SDK home
milestone keeps the SDK self-contained).

`strum = "0.26"` is a `default-features = false` SDK dep.
brunota20 added a commit that referenced this pull request Jun 25, 2026
…BuildError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
and Major #2 (BuildError listed for both treatments).

`BuildError` is the cowprotocol-side rejection envelope returned when
`build_order_creation` cannot assemble an `OrderCreation` body. The
submission-failure warn log can now carry
`error_kind = unknown_marker | cowprotocol` directly off the enum
instead of growing a `match err { ... => "unknown_marker" ... }`
ladder in the call site.

`strum = "0.26"` (default-features = false) lands as a direct dep of
the twap-monitor module. The enum is `enum BuildError` (not `pub`)
but adding `non_exhaustive` is still the rubric default for error
enums: it documents intent and costs nothing at the single call site
inside this module.
brunota20 added a commit that referenced this pull request Jun 25, 2026
The supervisor's dispatch path is per-chain by construction
(`dispatch_block(block)` filters modules by `block.chain_id`
matching their `[[subscription]]` table), and the COW-1071 WS
reconnect tasks own one per-chain backoff timer each. Multi-chain
isolation is therefore structural, not derived. This PR locks the
guarantee into the test suite with two new integration tests + a
supervisor.rs docstring stating the invariant explicitly.

## New tests

`multi_chain_dispatch_isolates_modules_by_chain`:
- Boot two `example` modules with different `[[subscription]]`
  chain_ids (1 + 100).
- Dispatch a block on chain 1 -> only module-a receives it
  (dispatched=1, alive_count=2 unchanged).
- Dispatch a block on chain 100 -> only module-b receives it.
- Validates: subscription filter is per-chain; a block on one
  chain does not even enter modules subscribed to a different
  chain.

`multi_chain_poisoned_module_does_not_affect_other_chains`:
- Boot fuel-bomb (always-traps) on chain 1 + example (healthy)
  on chain 100, with `PoisonPolicy::new(2, 60s)`.
- Trap bomb #1 on chain 1 -> bomb dies, poisoned=0, example
  untouched.
- Dispatch on chain 100 -> example receives (1/1).
- Wait 1.1 s (bomb backoff window), trap bomb #2 -> poisoned=1.
- Dispatch on chain 100 again -> example STILL receives.
- Validates: a permanently-poisoned module on one chain does not
  consume restart slots, fuel, or scheduling attention from
  modules on any other chain.

Total wall-clock ~1.2 s for the second test (one backoff window).

## supervisor.rs docstring

The module-level comment now articulates the multi-chain isolation
invariant explicitly so a future reader of the dispatch path knows
the property is load-bearing.

## What this proves

Supervisor side (dispatch fast-path):
- Per-module `alive`, `failure_count`, `next_attempt`, `poisoned`
  are independent of which chain triggered the event.
- Subscription filter excludes mismatched modules before any
  dispatch / restart logic runs.

Upstream side (already proven by COW-1071's architecture):
- `open_block_streams` spawns one task per chain; tasks share no
  state. A chain-A WS drop changes only chain-A's task state.
- `open_log_streams` is per-(module, chain) -> even tighter
  isolation than block streams.

## Out of scope

- A unit test that "fakes a WS drop" on chain A while chain B
  keeps yielding. Requires mocking `ProviderPool::subscribe_blocks`
  which today goes through real alloy / tokio infrastructure. The
  COW-1064 (E2E 4-6h testnet) and COW-1031 (7-day soak) will
  exercise this path against live RPCs.
- Per-chain configurable backoff / health-window. Today the
  reconnect policy is workspace-wide; per-chain tuning is a 0.3
  follow-up.

## Workspace impact

- `cargo test --workspace` -> 163 host tests + 6 doctests passing
  (was 161 + 6; +2 from the new integration tests).
- `cargo clippy --all-targets --workspace -- -D warnings` clean.
- `cargo fmt --all --check` clean.

Linear: COW-1073. Ninth M4 issue landed; stacks on #42 (COW-1072).
brunota20 added a commit that referenced this pull request Jun 26, 2026
…terError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
(remaining enums introduced on the M5 multi-chain pass).

- `EnvVarError` (engine_config.rs): introduced with the COW-1071 env-var
  substitution path. Snake_case variant labels feed the boot-time
  `tracing::error!(error_kind = ...)` call sites in `main.rs`.
- `FilterError` (supervisor.rs): introduced with the M5 multi-chain
  log-filter parsing. Snake_case variant labels feed the
  `tracing::warn!(error_kind = ...)` log emitted when a
  `[[subscription]]` address or topic fails to parse.

The audit's M3 / M4 derives landed on the milestones that introduced
the enums; these two complete the workspace-wide IntoStaticStr pass
flagged in audit Major #1 on the milestones that own them.
jean-neiverth pushed a commit that referenced this pull request Jun 29, 2026
…terError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
(remaining enums introduced on the M5 multi-chain pass).

- `EnvVarError` (engine_config.rs): introduced with the COW-1071 env-var
  substitution path. Snake_case variant labels feed the boot-time
  `tracing::error!(error_kind = ...)` call sites in `main.rs`.
- `FilterError` (supervisor.rs): introduced with the M5 multi-chain
  log-filter parsing. Snake_case variant labels feed the
  `tracing::warn!(error_kind = ...)` log emitted when a
  `[[subscription]]` address or topic fails to parse.

The audit's M3 / M4 derives landed on the milestones that introduced
the enums; these two complete the workspace-wide IntoStaticStr pass
flagged in audit Major #1 on the milestones that own them.
jean-neiverth pushed a commit that referenced this pull request Jun 29, 2026
Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1.
Vendored rubric mandates `strum::IntoStaticStr` (with
`#[strum(serialize_all = "snake_case")]`) on every error enum so
`error_kind` labels on the `shepherd_chain_request_total` /
`shepherd_cow_api_*` counters stay in lock-step with the Rust source
of truth instead of growing a `match err { ... => "connect" ... }`
ladder per call site.

Enums covered on this milestone (the ones present on dev/m2-base):
- `nexum_engine::host::cow_orderbook::CowApiError`
- `nexum_engine::host::provider_pool::ProviderError`
- `nexum_engine::manifest::error::ParseError`
- `nexum_engine::engine_config::EngineConfigError`

Also adds `#[non_exhaustive]` to `CowApiError` and `ProviderError`
(audit Major #2). The other two already carried it.

`strum = "0.26"` lands as a direct dep on nexum-engine. The
workspace-deps hoist (audit P1, Major #5) is intentionally a separate
judgment call left to Bruno; this commit ships the substantive rubric
fix without coupling to the broader Cargo.toml restructure.
jean-neiverth pushed a commit that referenced this pull request Jun 30, 2026
Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1.
Vendored rubric mandates `strum::IntoStaticStr` (with
`#[strum(serialize_all = "snake_case")]`) on every error enum so
`error_kind` labels on the `shepherd_chain_request_total` /
`shepherd_cow_api_*` counters stay in lock-step with the Rust source
of truth instead of growing a `match err { ... => "connect" ... }`
ladder per call site.

Enums covered on this milestone (the ones present on dev/m2-base):
- `nexum_engine::host::cow_orderbook::CowApiError`
- `nexum_engine::host::provider_pool::ProviderError`
- `nexum_engine::manifest::error::ParseError`
- `nexum_engine::engine_config::EngineConfigError`

Also adds `#[non_exhaustive]` to `CowApiError` and `ProviderError`
(audit Major #2). The other two already carried it.

`strum = "0.26"` lands as a direct dep on nexum-engine. The
workspace-deps hoist (audit P1, Major #5) is intentionally a separate
judgment call left to Bruno; this commit ships the substantive rubric
fix without coupling to the broader Cargo.toml restructure.
jean-neiverth pushed a commit that referenced this pull request Jun 30, 2026
Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1.
Vendored rubric mandates `strum::IntoStaticStr` (with
`#[strum(serialize_all = "snake_case")]`) on every error enum so
`error_kind` labels on the `shepherd_chain_request_total` /
`shepherd_cow_api_*` counters stay in lock-step with the Rust source
of truth instead of growing a `match err { ... => "connect" ... }`
ladder per call site.

Enums covered on this milestone (the ones present on dev/m2-base):
- `nexum_engine::host::cow_orderbook::CowApiError`
- `nexum_engine::host::provider_pool::ProviderError`
- `nexum_engine::manifest::error::ParseError`
- `nexum_engine::engine_config::EngineConfigError`

Also adds `#[non_exhaustive]` to `CowApiError` and `ProviderError`
(audit Major #2). The other two already carried it.

`strum = "0.26"` lands as a direct dep on nexum-engine. The
workspace-deps hoist (audit P1, Major #5) is intentionally a separate
judgment call left to Bruno; this commit ships the substantive rubric
fix without coupling to the broader Cargo.toml restructure.
jean-neiverth pushed a commit that referenced this pull request Jun 30, 2026
…terError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
(remaining enums introduced on the M5 multi-chain pass).

- `EnvVarError` (engine_config.rs): introduced with the COW-1071 env-var
  substitution path. Snake_case variant labels feed the boot-time
  `tracing::error!(error_kind = ...)` call sites in `main.rs`.
- `FilterError` (supervisor.rs): introduced with the M5 multi-chain
  log-filter parsing. Snake_case variant labels feed the
  `tracing::warn!(error_kind = ...)` log emitted when a
  `[[subscription]]` address or topic fails to parse.

The audit's M3 / M4 derives landed on the milestones that introduced
the enums; these two complete the workspace-wide IntoStaticStr pass
flagged in audit Major #1 on the milestones that own them.
jean-neiverth pushed a commit that referenced this pull request Jun 30, 2026
…terError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
(remaining enums introduced on the M5 multi-chain pass).

- `EnvVarError` (engine_config.rs): introduced with the COW-1071 env-var
  substitution path. Snake_case variant labels feed the boot-time
  `tracing::error!(error_kind = ...)` call sites in `main.rs`.
- `FilterError` (supervisor.rs): introduced with the M5 multi-chain
  log-filter parsing. Snake_case variant labels feed the
  `tracing::warn!(error_kind = ...)` log emitted when a
  `[[subscription]]` address or topic fails to parse.

The audit's M3 / M4 derives landed on the milestones that introduced
the enums; these two complete the workspace-wide IntoStaticStr pass
flagged in audit Major #1 on the milestones that own them.
jean-neiverth pushed a commit that referenced this pull request Jun 30, 2026
…terError

Audit reference: milestone-rubric-grant-audit-2026-06-25.md, Major #1
(remaining enums introduced on the M5 multi-chain pass).

- `EnvVarError` (engine_config.rs): introduced with the COW-1071 env-var
  substitution path. Snake_case variant labels feed the boot-time
  `tracing::error!(error_kind = ...)` call sites in `main.rs`.
- `FilterError` (supervisor.rs): introduced with the M5 multi-chain
  log-filter parsing. Snake_case variant labels feed the
  `tracing::warn!(error_kind = ...)` log emitted when a
  `[[subscription]]` address or topic fails to parse.

The audit's M3 / M4 derives landed on the milestones that introduced
the enums; these two complete the workspace-wide IntoStaticStr pass
flagged in audit Major #1 on the milestones that own them.
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.

2 participants