From 3e34ea611df2e14a372f0f0c6b080b2893cb876b Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Fri, 29 May 2026 14:55:33 -0700 Subject: [PATCH 1/4] Rename Rust crates to engine Co-authored-by: Cursor --- {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/Cargo.lock | 0 {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/Cargo.toml | 0 {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/pyproject.toml | 0 .../src/coin_ops_py.rs | 0 .../src/cycle/cancel_py.rs | 0 .../src/cycle/inventory_py.rs | 0 .../src/cycle/managed_py.rs | 0 .../src/cycle/market_py.rs | 0 .../src/cycle/mod.rs | 0 .../src/cycle/offer_py.rs | 0 .../src/cycle/orchestration_py.rs | 0 .../src/cycle/reconcile_py.rs | 0 .../src/cycle/stale_sweep_py.rs | 0 .../src/cycle/strategy_counts_py.rs | 0 .../src/execution_py.rs | 0 {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/hex_py.rs | 0 {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/lib.rs | 0 .../src/notifications_py.rs | 0 .../src/offer_action_py.rs | 0 .../src/offer_bootstrap_py.rs | 0 .../src/offer_build_py.rs | 0 .../src/offer_request_py.rs | 0 .../src/py_utils/bootstrap.rs | 0 .../src/py_utils/bootstrap_marshal.rs | 0 .../src/py_utils/coin_ops.rs | 0 .../src/py_utils/common.rs | 0 .../src/py_utils/cycle.rs | 0 .../src/py_utils/mod.rs | 0 .../src/py_utils/offer_request.rs | 0 .../src/py_utils/policy.rs | 0 .../src/retry_py.rs | 0 .../src/strategy_py.rs | 0 {greenfloor-signer => greenfloor-engine}/Cargo.lock | 0 {greenfloor-signer => greenfloor-engine}/Cargo.toml | 0 {greenfloor-signer => greenfloor-engine}/src/bls/coins.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/keyring.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/keys.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/mixed_split.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/mod.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/offer.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/signing.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/spend.rs | 0 {greenfloor-signer => greenfloor-engine}/src/bls/xch_coin_op.rs | 0 .../src/coin_ops/effective_counts.rs | 0 .../src/coin_ops/fee_budget.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coin_ops/gate.rs | 0 .../src/coin_ops/inventory.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coin_ops/mod.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coin_ops/plan.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coin_ops/policy.rs | 0 .../src/coin_ops/selection.rs | 0 .../src/coin_ops/split_planning.rs | 0 .../src/coin_ops/wallet_coin.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coinset/api.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coinset/asset.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coinset/backend.rs | 0 .../src/coinset/coin_select.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coinset/mod.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coinset/msp.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coinset/presplit.rs | 0 {greenfloor-signer => greenfloor-engine}/src/coinset/xch.rs | 0 {greenfloor-signer => greenfloor-engine}/src/config.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/cancel.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/dispatch.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/execution.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/lifecycle.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/managed.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/market.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/mod.rs | 0 .../src/cycle/notifications.rs | 0 .../src/cycle/orchestration.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/reconcile.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/reseed.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/retry.rs | 0 {greenfloor-signer => greenfloor-engine}/src/cycle/strategy.rs | 0 .../src/cycle/strategy_action.rs | 0 {greenfloor-signer => greenfloor-engine}/src/error/bls.rs | 0 {greenfloor-signer => greenfloor-engine}/src/error/mod.rs | 0 {greenfloor-signer => greenfloor-engine}/src/hex.rs | 0 {greenfloor-signer => greenfloor-engine}/src/kms.rs | 0 {greenfloor-signer => greenfloor-engine}/src/lib.rs | 0 {greenfloor-signer => greenfloor-engine}/src/main.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/action.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/assemble.rs | 0 .../src/offer/bootstrap/mod.rs | 0 .../src/offer/bootstrap/phase.rs | 0 .../src/offer/bootstrap/planner.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/build.rs | 0 .../src/offer/build_context.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/codec.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/invariants.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/mod.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/plan.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/presplit.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/publish.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/request.rs | 0 {greenfloor-signer => greenfloor-engine}/src/offer/types.rs | 0 .../src/test_support/export_fixtures_test.rs | 0 .../src/test_support/golden.rs | 0 {greenfloor-signer => greenfloor-engine}/src/test_support/mod.rs | 0 .../src/test_support/noop_coinset.rs | 0 .../src/test_support/simulator/coinset_backend.rs | 0 .../src/test_support/simulator/harness.rs | 0 .../src/test_support/simulator/mod.rs | 0 .../src/test_support/simulator/offer_roundtrips.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/context.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/materialize.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/members.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/messages.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/mixed_split.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/mod.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/session.rs | 0 {greenfloor-signer => greenfloor-engine}/src/vault/spend.rs | 0 ...igner_integration.py => test_greenfloor_engine_integration.py} | 0 ...hash_parity.py => test_greenfloor_engine_vault_hash_parity.py} | 0 115 files changed, 0 insertions(+), 0 deletions(-) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/Cargo.lock (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/Cargo.toml (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/pyproject.toml (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/coin_ops_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/cancel_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/inventory_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/managed_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/market_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/mod.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/offer_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/orchestration_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/reconcile_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/stale_sweep_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/cycle/strategy_counts_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/execution_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/hex_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/lib.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/notifications_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/offer_action_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/offer_bootstrap_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/offer_build_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/offer_request_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/bootstrap.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/bootstrap_marshal.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/coin_ops.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/common.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/cycle.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/mod.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/offer_request.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/py_utils/policy.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/retry_py.rs (100%) rename {greenfloor-signer-pyo3 => greenfloor-engine-pyo3}/src/strategy_py.rs (100%) rename {greenfloor-signer => greenfloor-engine}/Cargo.lock (100%) rename {greenfloor-signer => greenfloor-engine}/Cargo.toml (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/coins.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/keyring.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/keys.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/mixed_split.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/offer.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/signing.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/spend.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/bls/xch_coin_op.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/effective_counts.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/fee_budget.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/gate.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/inventory.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/plan.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/policy.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/selection.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/split_planning.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coin_ops/wallet_coin.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/api.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/asset.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/backend.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/coin_select.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/msp.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/presplit.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/coinset/xch.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/config.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/cancel.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/dispatch.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/execution.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/lifecycle.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/managed.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/market.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/notifications.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/orchestration.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/reconcile.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/reseed.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/retry.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/strategy.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/cycle/strategy_action.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/error/bls.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/error/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/hex.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/kms.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/lib.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/main.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/action.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/assemble.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/bootstrap/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/bootstrap/phase.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/bootstrap/planner.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/build.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/build_context.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/codec.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/invariants.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/plan.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/presplit.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/publish.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/request.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/offer/types.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/export_fixtures_test.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/golden.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/noop_coinset.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/simulator/coinset_backend.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/simulator/harness.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/simulator/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/test_support/simulator/offer_roundtrips.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/context.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/materialize.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/members.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/messages.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/mixed_split.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/mod.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/session.rs (100%) rename {greenfloor-signer => greenfloor-engine}/src/vault/spend.rs (100%) rename tests/{test_greenfloor_signer_integration.py => test_greenfloor_engine_integration.py} (100%) rename tests/{test_greenfloor_signer_vault_hash_parity.py => test_greenfloor_engine_vault_hash_parity.py} (100%) diff --git a/greenfloor-signer-pyo3/Cargo.lock b/greenfloor-engine-pyo3/Cargo.lock similarity index 100% rename from greenfloor-signer-pyo3/Cargo.lock rename to greenfloor-engine-pyo3/Cargo.lock diff --git a/greenfloor-signer-pyo3/Cargo.toml b/greenfloor-engine-pyo3/Cargo.toml similarity index 100% rename from greenfloor-signer-pyo3/Cargo.toml rename to greenfloor-engine-pyo3/Cargo.toml diff --git a/greenfloor-signer-pyo3/pyproject.toml b/greenfloor-engine-pyo3/pyproject.toml similarity index 100% rename from greenfloor-signer-pyo3/pyproject.toml rename to greenfloor-engine-pyo3/pyproject.toml diff --git a/greenfloor-signer-pyo3/src/coin_ops_py.rs b/greenfloor-engine-pyo3/src/coin_ops_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/coin_ops_py.rs rename to greenfloor-engine-pyo3/src/coin_ops_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/cancel_py.rs b/greenfloor-engine-pyo3/src/cycle/cancel_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/cancel_py.rs rename to greenfloor-engine-pyo3/src/cycle/cancel_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/inventory_py.rs b/greenfloor-engine-pyo3/src/cycle/inventory_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/inventory_py.rs rename to greenfloor-engine-pyo3/src/cycle/inventory_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/managed_py.rs b/greenfloor-engine-pyo3/src/cycle/managed_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/managed_py.rs rename to greenfloor-engine-pyo3/src/cycle/managed_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/market_py.rs b/greenfloor-engine-pyo3/src/cycle/market_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/market_py.rs rename to greenfloor-engine-pyo3/src/cycle/market_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/mod.rs b/greenfloor-engine-pyo3/src/cycle/mod.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/mod.rs rename to greenfloor-engine-pyo3/src/cycle/mod.rs diff --git a/greenfloor-signer-pyo3/src/cycle/offer_py.rs b/greenfloor-engine-pyo3/src/cycle/offer_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/offer_py.rs rename to greenfloor-engine-pyo3/src/cycle/offer_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/orchestration_py.rs b/greenfloor-engine-pyo3/src/cycle/orchestration_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/orchestration_py.rs rename to greenfloor-engine-pyo3/src/cycle/orchestration_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/reconcile_py.rs b/greenfloor-engine-pyo3/src/cycle/reconcile_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/reconcile_py.rs rename to greenfloor-engine-pyo3/src/cycle/reconcile_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/stale_sweep_py.rs b/greenfloor-engine-pyo3/src/cycle/stale_sweep_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/stale_sweep_py.rs rename to greenfloor-engine-pyo3/src/cycle/stale_sweep_py.rs diff --git a/greenfloor-signer-pyo3/src/cycle/strategy_counts_py.rs b/greenfloor-engine-pyo3/src/cycle/strategy_counts_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/cycle/strategy_counts_py.rs rename to greenfloor-engine-pyo3/src/cycle/strategy_counts_py.rs diff --git a/greenfloor-signer-pyo3/src/execution_py.rs b/greenfloor-engine-pyo3/src/execution_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/execution_py.rs rename to greenfloor-engine-pyo3/src/execution_py.rs diff --git a/greenfloor-signer-pyo3/src/hex_py.rs b/greenfloor-engine-pyo3/src/hex_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/hex_py.rs rename to greenfloor-engine-pyo3/src/hex_py.rs diff --git a/greenfloor-signer-pyo3/src/lib.rs b/greenfloor-engine-pyo3/src/lib.rs similarity index 100% rename from greenfloor-signer-pyo3/src/lib.rs rename to greenfloor-engine-pyo3/src/lib.rs diff --git a/greenfloor-signer-pyo3/src/notifications_py.rs b/greenfloor-engine-pyo3/src/notifications_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/notifications_py.rs rename to greenfloor-engine-pyo3/src/notifications_py.rs diff --git a/greenfloor-signer-pyo3/src/offer_action_py.rs b/greenfloor-engine-pyo3/src/offer_action_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/offer_action_py.rs rename to greenfloor-engine-pyo3/src/offer_action_py.rs diff --git a/greenfloor-signer-pyo3/src/offer_bootstrap_py.rs b/greenfloor-engine-pyo3/src/offer_bootstrap_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/offer_bootstrap_py.rs rename to greenfloor-engine-pyo3/src/offer_bootstrap_py.rs diff --git a/greenfloor-signer-pyo3/src/offer_build_py.rs b/greenfloor-engine-pyo3/src/offer_build_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/offer_build_py.rs rename to greenfloor-engine-pyo3/src/offer_build_py.rs diff --git a/greenfloor-signer-pyo3/src/offer_request_py.rs b/greenfloor-engine-pyo3/src/offer_request_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/offer_request_py.rs rename to greenfloor-engine-pyo3/src/offer_request_py.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/bootstrap.rs b/greenfloor-engine-pyo3/src/py_utils/bootstrap.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/bootstrap.rs rename to greenfloor-engine-pyo3/src/py_utils/bootstrap.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/bootstrap_marshal.rs b/greenfloor-engine-pyo3/src/py_utils/bootstrap_marshal.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/bootstrap_marshal.rs rename to greenfloor-engine-pyo3/src/py_utils/bootstrap_marshal.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/coin_ops.rs b/greenfloor-engine-pyo3/src/py_utils/coin_ops.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/coin_ops.rs rename to greenfloor-engine-pyo3/src/py_utils/coin_ops.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/common.rs b/greenfloor-engine-pyo3/src/py_utils/common.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/common.rs rename to greenfloor-engine-pyo3/src/py_utils/common.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/cycle.rs b/greenfloor-engine-pyo3/src/py_utils/cycle.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/cycle.rs rename to greenfloor-engine-pyo3/src/py_utils/cycle.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/mod.rs b/greenfloor-engine-pyo3/src/py_utils/mod.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/mod.rs rename to greenfloor-engine-pyo3/src/py_utils/mod.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/offer_request.rs b/greenfloor-engine-pyo3/src/py_utils/offer_request.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/offer_request.rs rename to greenfloor-engine-pyo3/src/py_utils/offer_request.rs diff --git a/greenfloor-signer-pyo3/src/py_utils/policy.rs b/greenfloor-engine-pyo3/src/py_utils/policy.rs similarity index 100% rename from greenfloor-signer-pyo3/src/py_utils/policy.rs rename to greenfloor-engine-pyo3/src/py_utils/policy.rs diff --git a/greenfloor-signer-pyo3/src/retry_py.rs b/greenfloor-engine-pyo3/src/retry_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/retry_py.rs rename to greenfloor-engine-pyo3/src/retry_py.rs diff --git a/greenfloor-signer-pyo3/src/strategy_py.rs b/greenfloor-engine-pyo3/src/strategy_py.rs similarity index 100% rename from greenfloor-signer-pyo3/src/strategy_py.rs rename to greenfloor-engine-pyo3/src/strategy_py.rs diff --git a/greenfloor-signer/Cargo.lock b/greenfloor-engine/Cargo.lock similarity index 100% rename from greenfloor-signer/Cargo.lock rename to greenfloor-engine/Cargo.lock diff --git a/greenfloor-signer/Cargo.toml b/greenfloor-engine/Cargo.toml similarity index 100% rename from greenfloor-signer/Cargo.toml rename to greenfloor-engine/Cargo.toml diff --git a/greenfloor-signer/src/bls/coins.rs b/greenfloor-engine/src/bls/coins.rs similarity index 100% rename from greenfloor-signer/src/bls/coins.rs rename to greenfloor-engine/src/bls/coins.rs diff --git a/greenfloor-signer/src/bls/keyring.rs b/greenfloor-engine/src/bls/keyring.rs similarity index 100% rename from greenfloor-signer/src/bls/keyring.rs rename to greenfloor-engine/src/bls/keyring.rs diff --git a/greenfloor-signer/src/bls/keys.rs b/greenfloor-engine/src/bls/keys.rs similarity index 100% rename from greenfloor-signer/src/bls/keys.rs rename to greenfloor-engine/src/bls/keys.rs diff --git a/greenfloor-signer/src/bls/mixed_split.rs b/greenfloor-engine/src/bls/mixed_split.rs similarity index 100% rename from greenfloor-signer/src/bls/mixed_split.rs rename to greenfloor-engine/src/bls/mixed_split.rs diff --git a/greenfloor-signer/src/bls/mod.rs b/greenfloor-engine/src/bls/mod.rs similarity index 100% rename from greenfloor-signer/src/bls/mod.rs rename to greenfloor-engine/src/bls/mod.rs diff --git a/greenfloor-signer/src/bls/offer.rs b/greenfloor-engine/src/bls/offer.rs similarity index 100% rename from greenfloor-signer/src/bls/offer.rs rename to greenfloor-engine/src/bls/offer.rs diff --git a/greenfloor-signer/src/bls/signing.rs b/greenfloor-engine/src/bls/signing.rs similarity index 100% rename from greenfloor-signer/src/bls/signing.rs rename to greenfloor-engine/src/bls/signing.rs diff --git a/greenfloor-signer/src/bls/spend.rs b/greenfloor-engine/src/bls/spend.rs similarity index 100% rename from greenfloor-signer/src/bls/spend.rs rename to greenfloor-engine/src/bls/spend.rs diff --git a/greenfloor-signer/src/bls/xch_coin_op.rs b/greenfloor-engine/src/bls/xch_coin_op.rs similarity index 100% rename from greenfloor-signer/src/bls/xch_coin_op.rs rename to greenfloor-engine/src/bls/xch_coin_op.rs diff --git a/greenfloor-signer/src/coin_ops/effective_counts.rs b/greenfloor-engine/src/coin_ops/effective_counts.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/effective_counts.rs rename to greenfloor-engine/src/coin_ops/effective_counts.rs diff --git a/greenfloor-signer/src/coin_ops/fee_budget.rs b/greenfloor-engine/src/coin_ops/fee_budget.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/fee_budget.rs rename to greenfloor-engine/src/coin_ops/fee_budget.rs diff --git a/greenfloor-signer/src/coin_ops/gate.rs b/greenfloor-engine/src/coin_ops/gate.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/gate.rs rename to greenfloor-engine/src/coin_ops/gate.rs diff --git a/greenfloor-signer/src/coin_ops/inventory.rs b/greenfloor-engine/src/coin_ops/inventory.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/inventory.rs rename to greenfloor-engine/src/coin_ops/inventory.rs diff --git a/greenfloor-signer/src/coin_ops/mod.rs b/greenfloor-engine/src/coin_ops/mod.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/mod.rs rename to greenfloor-engine/src/coin_ops/mod.rs diff --git a/greenfloor-signer/src/coin_ops/plan.rs b/greenfloor-engine/src/coin_ops/plan.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/plan.rs rename to greenfloor-engine/src/coin_ops/plan.rs diff --git a/greenfloor-signer/src/coin_ops/policy.rs b/greenfloor-engine/src/coin_ops/policy.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/policy.rs rename to greenfloor-engine/src/coin_ops/policy.rs diff --git a/greenfloor-signer/src/coin_ops/selection.rs b/greenfloor-engine/src/coin_ops/selection.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/selection.rs rename to greenfloor-engine/src/coin_ops/selection.rs diff --git a/greenfloor-signer/src/coin_ops/split_planning.rs b/greenfloor-engine/src/coin_ops/split_planning.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/split_planning.rs rename to greenfloor-engine/src/coin_ops/split_planning.rs diff --git a/greenfloor-signer/src/coin_ops/wallet_coin.rs b/greenfloor-engine/src/coin_ops/wallet_coin.rs similarity index 100% rename from greenfloor-signer/src/coin_ops/wallet_coin.rs rename to greenfloor-engine/src/coin_ops/wallet_coin.rs diff --git a/greenfloor-signer/src/coinset/api.rs b/greenfloor-engine/src/coinset/api.rs similarity index 100% rename from greenfloor-signer/src/coinset/api.rs rename to greenfloor-engine/src/coinset/api.rs diff --git a/greenfloor-signer/src/coinset/asset.rs b/greenfloor-engine/src/coinset/asset.rs similarity index 100% rename from greenfloor-signer/src/coinset/asset.rs rename to greenfloor-engine/src/coinset/asset.rs diff --git a/greenfloor-signer/src/coinset/backend.rs b/greenfloor-engine/src/coinset/backend.rs similarity index 100% rename from greenfloor-signer/src/coinset/backend.rs rename to greenfloor-engine/src/coinset/backend.rs diff --git a/greenfloor-signer/src/coinset/coin_select.rs b/greenfloor-engine/src/coinset/coin_select.rs similarity index 100% rename from greenfloor-signer/src/coinset/coin_select.rs rename to greenfloor-engine/src/coinset/coin_select.rs diff --git a/greenfloor-signer/src/coinset/mod.rs b/greenfloor-engine/src/coinset/mod.rs similarity index 100% rename from greenfloor-signer/src/coinset/mod.rs rename to greenfloor-engine/src/coinset/mod.rs diff --git a/greenfloor-signer/src/coinset/msp.rs b/greenfloor-engine/src/coinset/msp.rs similarity index 100% rename from greenfloor-signer/src/coinset/msp.rs rename to greenfloor-engine/src/coinset/msp.rs diff --git a/greenfloor-signer/src/coinset/presplit.rs b/greenfloor-engine/src/coinset/presplit.rs similarity index 100% rename from greenfloor-signer/src/coinset/presplit.rs rename to greenfloor-engine/src/coinset/presplit.rs diff --git a/greenfloor-signer/src/coinset/xch.rs b/greenfloor-engine/src/coinset/xch.rs similarity index 100% rename from greenfloor-signer/src/coinset/xch.rs rename to greenfloor-engine/src/coinset/xch.rs diff --git a/greenfloor-signer/src/config.rs b/greenfloor-engine/src/config.rs similarity index 100% rename from greenfloor-signer/src/config.rs rename to greenfloor-engine/src/config.rs diff --git a/greenfloor-signer/src/cycle/cancel.rs b/greenfloor-engine/src/cycle/cancel.rs similarity index 100% rename from greenfloor-signer/src/cycle/cancel.rs rename to greenfloor-engine/src/cycle/cancel.rs diff --git a/greenfloor-signer/src/cycle/dispatch.rs b/greenfloor-engine/src/cycle/dispatch.rs similarity index 100% rename from greenfloor-signer/src/cycle/dispatch.rs rename to greenfloor-engine/src/cycle/dispatch.rs diff --git a/greenfloor-signer/src/cycle/execution.rs b/greenfloor-engine/src/cycle/execution.rs similarity index 100% rename from greenfloor-signer/src/cycle/execution.rs rename to greenfloor-engine/src/cycle/execution.rs diff --git a/greenfloor-signer/src/cycle/lifecycle.rs b/greenfloor-engine/src/cycle/lifecycle.rs similarity index 100% rename from greenfloor-signer/src/cycle/lifecycle.rs rename to greenfloor-engine/src/cycle/lifecycle.rs diff --git a/greenfloor-signer/src/cycle/managed.rs b/greenfloor-engine/src/cycle/managed.rs similarity index 100% rename from greenfloor-signer/src/cycle/managed.rs rename to greenfloor-engine/src/cycle/managed.rs diff --git a/greenfloor-signer/src/cycle/market.rs b/greenfloor-engine/src/cycle/market.rs similarity index 100% rename from greenfloor-signer/src/cycle/market.rs rename to greenfloor-engine/src/cycle/market.rs diff --git a/greenfloor-signer/src/cycle/mod.rs b/greenfloor-engine/src/cycle/mod.rs similarity index 100% rename from greenfloor-signer/src/cycle/mod.rs rename to greenfloor-engine/src/cycle/mod.rs diff --git a/greenfloor-signer/src/cycle/notifications.rs b/greenfloor-engine/src/cycle/notifications.rs similarity index 100% rename from greenfloor-signer/src/cycle/notifications.rs rename to greenfloor-engine/src/cycle/notifications.rs diff --git a/greenfloor-signer/src/cycle/orchestration.rs b/greenfloor-engine/src/cycle/orchestration.rs similarity index 100% rename from greenfloor-signer/src/cycle/orchestration.rs rename to greenfloor-engine/src/cycle/orchestration.rs diff --git a/greenfloor-signer/src/cycle/reconcile.rs b/greenfloor-engine/src/cycle/reconcile.rs similarity index 100% rename from greenfloor-signer/src/cycle/reconcile.rs rename to greenfloor-engine/src/cycle/reconcile.rs diff --git a/greenfloor-signer/src/cycle/reseed.rs b/greenfloor-engine/src/cycle/reseed.rs similarity index 100% rename from greenfloor-signer/src/cycle/reseed.rs rename to greenfloor-engine/src/cycle/reseed.rs diff --git a/greenfloor-signer/src/cycle/retry.rs b/greenfloor-engine/src/cycle/retry.rs similarity index 100% rename from greenfloor-signer/src/cycle/retry.rs rename to greenfloor-engine/src/cycle/retry.rs diff --git a/greenfloor-signer/src/cycle/strategy.rs b/greenfloor-engine/src/cycle/strategy.rs similarity index 100% rename from greenfloor-signer/src/cycle/strategy.rs rename to greenfloor-engine/src/cycle/strategy.rs diff --git a/greenfloor-signer/src/cycle/strategy_action.rs b/greenfloor-engine/src/cycle/strategy_action.rs similarity index 100% rename from greenfloor-signer/src/cycle/strategy_action.rs rename to greenfloor-engine/src/cycle/strategy_action.rs diff --git a/greenfloor-signer/src/error/bls.rs b/greenfloor-engine/src/error/bls.rs similarity index 100% rename from greenfloor-signer/src/error/bls.rs rename to greenfloor-engine/src/error/bls.rs diff --git a/greenfloor-signer/src/error/mod.rs b/greenfloor-engine/src/error/mod.rs similarity index 100% rename from greenfloor-signer/src/error/mod.rs rename to greenfloor-engine/src/error/mod.rs diff --git a/greenfloor-signer/src/hex.rs b/greenfloor-engine/src/hex.rs similarity index 100% rename from greenfloor-signer/src/hex.rs rename to greenfloor-engine/src/hex.rs diff --git a/greenfloor-signer/src/kms.rs b/greenfloor-engine/src/kms.rs similarity index 100% rename from greenfloor-signer/src/kms.rs rename to greenfloor-engine/src/kms.rs diff --git a/greenfloor-signer/src/lib.rs b/greenfloor-engine/src/lib.rs similarity index 100% rename from greenfloor-signer/src/lib.rs rename to greenfloor-engine/src/lib.rs diff --git a/greenfloor-signer/src/main.rs b/greenfloor-engine/src/main.rs similarity index 100% rename from greenfloor-signer/src/main.rs rename to greenfloor-engine/src/main.rs diff --git a/greenfloor-signer/src/offer/action.rs b/greenfloor-engine/src/offer/action.rs similarity index 100% rename from greenfloor-signer/src/offer/action.rs rename to greenfloor-engine/src/offer/action.rs diff --git a/greenfloor-signer/src/offer/assemble.rs b/greenfloor-engine/src/offer/assemble.rs similarity index 100% rename from greenfloor-signer/src/offer/assemble.rs rename to greenfloor-engine/src/offer/assemble.rs diff --git a/greenfloor-signer/src/offer/bootstrap/mod.rs b/greenfloor-engine/src/offer/bootstrap/mod.rs similarity index 100% rename from greenfloor-signer/src/offer/bootstrap/mod.rs rename to greenfloor-engine/src/offer/bootstrap/mod.rs diff --git a/greenfloor-signer/src/offer/bootstrap/phase.rs b/greenfloor-engine/src/offer/bootstrap/phase.rs similarity index 100% rename from greenfloor-signer/src/offer/bootstrap/phase.rs rename to greenfloor-engine/src/offer/bootstrap/phase.rs diff --git a/greenfloor-signer/src/offer/bootstrap/planner.rs b/greenfloor-engine/src/offer/bootstrap/planner.rs similarity index 100% rename from greenfloor-signer/src/offer/bootstrap/planner.rs rename to greenfloor-engine/src/offer/bootstrap/planner.rs diff --git a/greenfloor-signer/src/offer/build.rs b/greenfloor-engine/src/offer/build.rs similarity index 100% rename from greenfloor-signer/src/offer/build.rs rename to greenfloor-engine/src/offer/build.rs diff --git a/greenfloor-signer/src/offer/build_context.rs b/greenfloor-engine/src/offer/build_context.rs similarity index 100% rename from greenfloor-signer/src/offer/build_context.rs rename to greenfloor-engine/src/offer/build_context.rs diff --git a/greenfloor-signer/src/offer/codec.rs b/greenfloor-engine/src/offer/codec.rs similarity index 100% rename from greenfloor-signer/src/offer/codec.rs rename to greenfloor-engine/src/offer/codec.rs diff --git a/greenfloor-signer/src/offer/invariants.rs b/greenfloor-engine/src/offer/invariants.rs similarity index 100% rename from greenfloor-signer/src/offer/invariants.rs rename to greenfloor-engine/src/offer/invariants.rs diff --git a/greenfloor-signer/src/offer/mod.rs b/greenfloor-engine/src/offer/mod.rs similarity index 100% rename from greenfloor-signer/src/offer/mod.rs rename to greenfloor-engine/src/offer/mod.rs diff --git a/greenfloor-signer/src/offer/plan.rs b/greenfloor-engine/src/offer/plan.rs similarity index 100% rename from greenfloor-signer/src/offer/plan.rs rename to greenfloor-engine/src/offer/plan.rs diff --git a/greenfloor-signer/src/offer/presplit.rs b/greenfloor-engine/src/offer/presplit.rs similarity index 100% rename from greenfloor-signer/src/offer/presplit.rs rename to greenfloor-engine/src/offer/presplit.rs diff --git a/greenfloor-signer/src/offer/publish.rs b/greenfloor-engine/src/offer/publish.rs similarity index 100% rename from greenfloor-signer/src/offer/publish.rs rename to greenfloor-engine/src/offer/publish.rs diff --git a/greenfloor-signer/src/offer/request.rs b/greenfloor-engine/src/offer/request.rs similarity index 100% rename from greenfloor-signer/src/offer/request.rs rename to greenfloor-engine/src/offer/request.rs diff --git a/greenfloor-signer/src/offer/types.rs b/greenfloor-engine/src/offer/types.rs similarity index 100% rename from greenfloor-signer/src/offer/types.rs rename to greenfloor-engine/src/offer/types.rs diff --git a/greenfloor-signer/src/test_support/export_fixtures_test.rs b/greenfloor-engine/src/test_support/export_fixtures_test.rs similarity index 100% rename from greenfloor-signer/src/test_support/export_fixtures_test.rs rename to greenfloor-engine/src/test_support/export_fixtures_test.rs diff --git a/greenfloor-signer/src/test_support/golden.rs b/greenfloor-engine/src/test_support/golden.rs similarity index 100% rename from greenfloor-signer/src/test_support/golden.rs rename to greenfloor-engine/src/test_support/golden.rs diff --git a/greenfloor-signer/src/test_support/mod.rs b/greenfloor-engine/src/test_support/mod.rs similarity index 100% rename from greenfloor-signer/src/test_support/mod.rs rename to greenfloor-engine/src/test_support/mod.rs diff --git a/greenfloor-signer/src/test_support/noop_coinset.rs b/greenfloor-engine/src/test_support/noop_coinset.rs similarity index 100% rename from greenfloor-signer/src/test_support/noop_coinset.rs rename to greenfloor-engine/src/test_support/noop_coinset.rs diff --git a/greenfloor-signer/src/test_support/simulator/coinset_backend.rs b/greenfloor-engine/src/test_support/simulator/coinset_backend.rs similarity index 100% rename from greenfloor-signer/src/test_support/simulator/coinset_backend.rs rename to greenfloor-engine/src/test_support/simulator/coinset_backend.rs diff --git a/greenfloor-signer/src/test_support/simulator/harness.rs b/greenfloor-engine/src/test_support/simulator/harness.rs similarity index 100% rename from greenfloor-signer/src/test_support/simulator/harness.rs rename to greenfloor-engine/src/test_support/simulator/harness.rs diff --git a/greenfloor-signer/src/test_support/simulator/mod.rs b/greenfloor-engine/src/test_support/simulator/mod.rs similarity index 100% rename from greenfloor-signer/src/test_support/simulator/mod.rs rename to greenfloor-engine/src/test_support/simulator/mod.rs diff --git a/greenfloor-signer/src/test_support/simulator/offer_roundtrips.rs b/greenfloor-engine/src/test_support/simulator/offer_roundtrips.rs similarity index 100% rename from greenfloor-signer/src/test_support/simulator/offer_roundtrips.rs rename to greenfloor-engine/src/test_support/simulator/offer_roundtrips.rs diff --git a/greenfloor-signer/src/vault/context.rs b/greenfloor-engine/src/vault/context.rs similarity index 100% rename from greenfloor-signer/src/vault/context.rs rename to greenfloor-engine/src/vault/context.rs diff --git a/greenfloor-signer/src/vault/materialize.rs b/greenfloor-engine/src/vault/materialize.rs similarity index 100% rename from greenfloor-signer/src/vault/materialize.rs rename to greenfloor-engine/src/vault/materialize.rs diff --git a/greenfloor-signer/src/vault/members.rs b/greenfloor-engine/src/vault/members.rs similarity index 100% rename from greenfloor-signer/src/vault/members.rs rename to greenfloor-engine/src/vault/members.rs diff --git a/greenfloor-signer/src/vault/messages.rs b/greenfloor-engine/src/vault/messages.rs similarity index 100% rename from greenfloor-signer/src/vault/messages.rs rename to greenfloor-engine/src/vault/messages.rs diff --git a/greenfloor-signer/src/vault/mixed_split.rs b/greenfloor-engine/src/vault/mixed_split.rs similarity index 100% rename from greenfloor-signer/src/vault/mixed_split.rs rename to greenfloor-engine/src/vault/mixed_split.rs diff --git a/greenfloor-signer/src/vault/mod.rs b/greenfloor-engine/src/vault/mod.rs similarity index 100% rename from greenfloor-signer/src/vault/mod.rs rename to greenfloor-engine/src/vault/mod.rs diff --git a/greenfloor-signer/src/vault/session.rs b/greenfloor-engine/src/vault/session.rs similarity index 100% rename from greenfloor-signer/src/vault/session.rs rename to greenfloor-engine/src/vault/session.rs diff --git a/greenfloor-signer/src/vault/spend.rs b/greenfloor-engine/src/vault/spend.rs similarity index 100% rename from greenfloor-signer/src/vault/spend.rs rename to greenfloor-engine/src/vault/spend.rs diff --git a/tests/test_greenfloor_signer_integration.py b/tests/test_greenfloor_engine_integration.py similarity index 100% rename from tests/test_greenfloor_signer_integration.py rename to tests/test_greenfloor_engine_integration.py diff --git a/tests/test_greenfloor_signer_vault_hash_parity.py b/tests/test_greenfloor_engine_vault_hash_parity.py similarity index 100% rename from tests/test_greenfloor_signer_vault_hash_parity.py rename to tests/test_greenfloor_engine_vault_hash_parity.py From 37ea12eeb0673dca21ad0791f37b742c619ea9bd Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Fri, 29 May 2026 15:01:56 -0700 Subject: [PATCH 2/4] Complete Rust engine rename Co-authored-by: Cursor --- .../greenfloor-maturin-wheels/action.yml | 10 +- .../actions/rust-cache-greenfloor/action.yml | 4 +- .github/workflows/ci.yml | 14 +- .gitignore | 6 +- AGENTS.md | 6 +- config/program.yaml | 2 +- .../0002-signing-pipeline-consolidation.md | 2 +- .../0006-rust-signer-canonical-path.md | 10 +- .../0007-rust-signer-pyo3-boundary.md | 6 +- .../0010-rust-engine-crate-naming.md | 52 +++ .../0010-rust-kernel-crate-naming.md | 51 --- ...-offer-request-python-import-boundaries.md | 28 +- docs/plan.md | 4 +- docs/progress.md | 208 +++++----- greenfloor-engine-pyo3/Cargo.lock | 6 +- greenfloor-engine-pyo3/Cargo.toml | 6 +- greenfloor-engine-pyo3/pyproject.toml | 4 +- greenfloor-engine-pyo3/src/coin_ops_py.rs | 2 +- greenfloor-engine-pyo3/src/cycle/cancel_py.rs | 2 +- .../src/cycle/inventory_py.rs | 2 +- .../src/cycle/managed_py.rs | 4 +- greenfloor-engine-pyo3/src/cycle/market_py.rs | 2 +- greenfloor-engine-pyo3/src/cycle/offer_py.rs | 2 +- .../src/cycle/orchestration_py.rs | 2 +- .../src/cycle/reconcile_py.rs | 2 +- .../src/cycle/stale_sweep_py.rs | 2 +- .../src/cycle/strategy_counts_py.rs | 2 +- greenfloor-engine-pyo3/src/execution_py.rs | 2 +- greenfloor-engine-pyo3/src/hex_py.rs | 2 +- greenfloor-engine-pyo3/src/lib.rs | 27 +- .../src/notifications_py.rs | 2 +- greenfloor-engine-pyo3/src/offer_action_py.rs | 10 +- greenfloor-engine-pyo3/src/offer_build_py.rs | 2 +- .../src/offer_request_py.rs | 2 +- .../src/py_utils/bootstrap.rs | 2 +- .../src/py_utils/bootstrap_marshal.rs | 2 +- .../src/py_utils/coin_ops.rs | 32 +- greenfloor-engine-pyo3/src/py_utils/cycle.rs | 14 +- .../src/py_utils/offer_request.rs | 2 +- greenfloor-engine-pyo3/src/py_utils/policy.rs | 12 +- greenfloor-engine-pyo3/src/retry_py.rs | 2 +- greenfloor-engine-pyo3/src/strategy_py.rs | 2 +- greenfloor-engine/Cargo.lock | 2 +- greenfloor-engine/Cargo.toml | 8 +- greenfloor-engine/src/coin_ops/mod.rs | 3 +- greenfloor-engine/src/cycle/mod.rs | 2 +- greenfloor-engine/src/lib.rs | 8 +- greenfloor-engine/src/main.rs | 26 +- greenfloor-engine/src/offer/mod.rs | 2 +- greenfloor/adapters/bls_cat_coins.py | 14 +- greenfloor/adapters/bls_signing.py | 24 +- greenfloor/adapters/coinset.py | 16 +- greenfloor/adapters/native_offer.py | 12 +- greenfloor/adapters/offer_action.py | 12 +- greenfloor/adapters/rust_signer.py | 28 +- greenfloor/core/cancel_policy.py | 10 +- greenfloor/core/coin_ops/__init__.py | 2 +- greenfloor/core/coin_ops/_bridge.py | 48 +-- greenfloor/core/coin_ops/engine_protocol.py | 131 ++++++ greenfloor/core/coin_ops/kernel_protocol.py | 132 +----- greenfloor/core/coin_ops/selection_bridge.py | 20 +- greenfloor/core/cycle/__init__.py | 4 +- greenfloor/core/cycle/_bridge_managed.py | 40 +- .../core/cycle/_bridge_orchestration.py | 52 +-- greenfloor/core/cycle_reseed.py | 2 +- greenfloor/core/engine_bridge.py | 124 ++++++ greenfloor/core/engine_maps.py | 22 + greenfloor/core/engine_protocol.py | 380 +++++++++++++++++ greenfloor/core/kernel_bridge.py | 130 +----- greenfloor/core/kernel_maps.py | 23 +- greenfloor/core/kernel_protocol.py | 390 ++---------------- greenfloor/core/notifications.py | 4 +- greenfloor/core/offer_action.py | 6 +- greenfloor/core/offer_bootstrap_bridge.py | 12 +- greenfloor/core/offer_reconcile/__init__.py | 12 +- greenfloor/core/offer_request_bridge.py | 8 +- greenfloor/core/planned_action.py | 4 +- greenfloor/core/policy_bridge.py | 30 +- greenfloor/core/signer_offer_request.py | 2 +- greenfloor/core/strategy_action_item.py | 2 +- greenfloor/offer_builder.py | 2 +- greenfloor/runtime/offer_action_build.py | 2 +- pyproject.toml | 2 +- scripts/export_signer_fixtures.py | 14 +- .../{kernel_mock.py => engine_mock.py} | 50 +-- tests/test_cancel_policy_kernel.py | 2 +- tests/test_coin_ops_policy_parity.py | 20 +- tests/test_cycle_reseed.py | 2 +- tests/test_engine_bridge.py | 96 +++++ tests/test_greenfloor_engine_integration.py | 42 +- ...est_greenfloor_engine_vault_hash_parity.py | 4 +- tests/test_greenfloor_native_contract.py | 10 +- tests/test_kernel_bridge.py | 96 ----- tests/test_manager_build_post_offer.py | 12 +- tests/test_offer_bootstrap.py | 4 +- tests/test_offer_publish.py | 54 +-- tests/test_signer_create_offer_parity.py | 41 +- tests/test_signing.py | 14 +- 98 files changed, 1431 insertions(+), 1339 deletions(-) create mode 100644 docs/decisions/0010-rust-engine-crate-naming.md delete mode 100644 docs/decisions/0010-rust-kernel-crate-naming.md create mode 100644 greenfloor/core/coin_ops/engine_protocol.py create mode 100644 greenfloor/core/engine_bridge.py create mode 100644 greenfloor/core/engine_maps.py create mode 100644 greenfloor/core/engine_protocol.py rename tests/helpers/{kernel_mock.py => engine_mock.py} (79%) create mode 100644 tests/test_engine_bridge.py delete mode 100644 tests/test_kernel_bridge.py diff --git a/.github/actions/greenfloor-maturin-wheels/action.yml b/.github/actions/greenfloor-maturin-wheels/action.yml index 273ae910..795ce1a4 100644 --- a/.github/actions/greenfloor-maturin-wheels/action.yml +++ b/.github/actions/greenfloor-maturin-wheels/action.yml @@ -1,6 +1,6 @@ --- name: GreenFloor maturin wheels -description: Ensure and optionally install the GreenFloor signer PyO3 wheel. +description: Ensure and optionally install the GreenFloor engine PyO3 wheel. inputs: operation: @@ -18,8 +18,8 @@ runs: with: operation: ${{ inputs.operation }} builder: maturin - tree-path: greenfloor-signer-pyo3 - source-path: greenfloor-signer-pyo3/Cargo.toml - cache-key-prefix: signer-pyo3-wheelhouse - wheelhouse-subdir: greenfloor-signer-pyo3 + tree-path: greenfloor-engine-pyo3 + source-path: greenfloor-engine-pyo3/Cargo.toml + cache-key-prefix: engine-pyo3-wheelhouse + wheelhouse-subdir: greenfloor-engine-pyo3 rust-already-configured: ${{ inputs.rust-already-configured }} diff --git a/.github/actions/rust-cache-greenfloor/action.yml b/.github/actions/rust-cache-greenfloor/action.yml index 8959aff8..58ecbd54 100644 --- a/.github/actions/rust-cache-greenfloor/action.yml +++ b/.github/actions/rust-cache-greenfloor/action.yml @@ -12,6 +12,6 @@ runs: - uses: Swatinem/rust-cache@v2 with: workspaces: | - greenfloor-signer -> target - greenfloor-signer-pyo3 -> target + greenfloor-engine -> target + greenfloor-engine-pyo3 -> target cache-workspace-crates: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b18ba98..67b11861 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,11 +70,11 @@ jobs: - uses: ./.github/actions/rust-cache-greenfloor if: matrix.run_rust_tests - - name: Build and test greenfloor-signer + - name: Build and test greenfloor-engine if: matrix.run_rust_tests run: | - cargo build --manifest-path greenfloor-signer/Cargo.toml - cargo test --manifest-path greenfloor-signer/Cargo.toml + cargo build --manifest-path greenfloor-engine/Cargo.toml + cargo test --manifest-path greenfloor-engine/Cargo.toml - uses: ./.github/actions/greenfloor-maturin-wheels with: @@ -98,9 +98,9 @@ jobs: - name: "Test suite (pytest)" run: pytest -v --tb=short - - name: Pytest (signer marker) + - name: Pytest (engine marker) if: matrix.run_rust_tests - run: pytest -v --tb=short -m signer + run: pytest -v --tb=short -m engine - uses: ./.github/actions/wheel-cache if: matrix.run_sdk_integration @@ -113,6 +113,6 @@ jobs: wheelhouse-subdir: chia-wallet-sdk rust-already-configured: ${{ matrix.run_rust_tests }} - - name: Pytest (greenfloor_signer + chia-wallet-sdk offer round-trip on Ubuntu) + - name: Pytest (greenfloor_engine + chia-wallet-sdk offer round-trip on Ubuntu) if: matrix.run_sdk_integration - run: GREENFLOOR_RUN_SIGNER_INTEGRATION_TESTS=1 pytest -vv -s tests/test_greenfloor_signer_integration.py + run: GREENFLOOR_RUN_ENGINE_INTEGRATION_TESTS=1 pytest -vv -s tests/test_greenfloor_engine_integration.py diff --git a/.gitignore b/.gitignore index 9e0bf5d0..4ef36451 100644 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,9 @@ dist/ build/ *.egg-info/ greenfloor-native/target/ -greenfloor-signer-pyo3/target/ -greenfloor-signer-pyo3/python/ -greenfloor-signer/target/ +greenfloor-engine-pyo3/target/ +greenfloor-engine-pyo3/python/ +greenfloor-engine/target/ .coverage .coverage.* htmlcov/ diff --git a/AGENTS.md b/AGENTS.md index c4d26a4f..0eea8c45 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,8 +36,8 @@ Severity tags: - `[MUST]` `greenfloor/core/coin_ops/`: coin-op deterministic policy (plan, fee budget, inventory, min-amount guard) shared by CLI and daemon. - `[MUST]` `greenfloor/config`: parse/validate config, resolve paths, resolve quote assets. - `[MUST]` `greenfloor/* adapters`: side effects only (network, filesystem, wallet, notifications). -- `[MUST]` Signing/execution path is adapter -> canonical Rust kernel (`greenfloor-signer` / `greenfloor_kernel` PyO3); legacy `greenfloor/signing.py` re-exports adapters only. -- `[MUST]` `greenfloor-signer/`: canonical vault KMS signing implementation; new vault spend/offer logic lands here first. +- `[MUST]` Signing/execution path is adapter -> canonical Rust engine (`greenfloor-engine` crate / `greenfloor_engine` PyO3); legacy `greenfloor/signing.py` re-exports adapters only. +- `[MUST]` `greenfloor-engine/`: canonical Rust engine crate; new vault spend/offer logic lands here first. - `[MUST]` `greenfloor/cli/manager.py`: operator CLI router (argparse + dispatch). - `[MUST]` `greenfloor/cli/coin_ops_list.py`, `coin_ops_split.py`, `coin_ops_combine.py`: coin list/split/combine CLI commands (`coin_ops.py` re-exports). - `[MUST]` `greenfloor/cli/cats.py`: local CAT catalog CLI commands. @@ -57,7 +57,7 @@ Severity tags: ## Design Constraints - `[MUST]` Prefer direct function calls within the package; do not spawn subprocesses for same-env Python calls unless isolation/security is documented in `docs/decisions/`. -- `[MUST]` Signing/execution path is adapter -> canonical Rust kernel (`greenfloor-signer` crate, `greenfloor_kernel` PyO3 module). +- `[MUST]` Signing/execution path is adapter -> canonical Rust engine (`greenfloor-engine` crate, `greenfloor_engine` PyO3 module). - `[MUST]` Avoid unnecessary indirection layers (`executor`, `worker`, `engine`, etc.). - `[MUST]` Keep one distinct responsibility per file; merge pass-through modules into functions. - `[MUST]` Eliminate duplicated logic blocks (>10 lines) by extracting shared helpers. diff --git a/config/program.yaml b/config/program.yaml index dc7fda83..48afdf23 100644 --- a/config/program.yaml +++ b/config/program.yaml @@ -22,7 +22,7 @@ runtime: market_slot_count: 4 dry_run: false -# Local Rust signer (greenfloor-signer) for vault KMS offer execution. +# Local Rust engine for vault KMS offer execution. # Written to ~/.greenfloor/config/signer.yaml at runtime for PyO3 load_signer_config. signer: coinset_msp_base_url: "https://api-msp.coinset.org" diff --git a/docs/decisions/0002-signing-pipeline-consolidation.md b/docs/decisions/0002-signing-pipeline-consolidation.md index 3047f2dc..16600cef 100644 --- a/docs/decisions/0002-signing-pipeline-consolidation.md +++ b/docs/decisions/0002-signing-pipeline-consolidation.md @@ -38,5 +38,5 @@ To avoid signing-path drift, the canonical in-process entrypoints are: New signing integrations should route through these APIs instead of introducing alternate signing stacks. -**Update (2026-05):** ADR 0006 makes `greenfloor-signer` the canonical vault signing +**Update (2026-05):** ADR 0006 makes `greenfloor-engine` the canonical vault signing implementation. Python APIs above are migration wrappers until Rust paths replace them. diff --git a/docs/decisions/0006-rust-signer-canonical-path.md b/docs/decisions/0006-rust-signer-canonical-path.md index 125d5894..6b0cb3d6 100644 --- a/docs/decisions/0006-rust-signer-canonical-path.md +++ b/docs/decisions/0006-rust-signer-canonical-path.md @@ -6,14 +6,14 @@ Accepted ## Decision -`greenfloor-signer` is the canonical signing implementation for vault KMS paths. +`greenfloor-engine` is the canonical signing implementation for vault KMS paths. Python signing in `greenfloor/signing.py` remains during migration but is **legacy**; new vault spend, mixed-split, and offer behavior lands in Rust first. Migration order: -1. **Now:** `greenfloor-signer` CLI for operator/debug flows and CI parity tests. -2. **Next:** Python daemon/CLI invoke `greenfloor-signer` (or a PyO3 wrapper) instead of +1. **Now:** `greenfloor-engine` CLI for operator/debug flows and CI parity tests. +2. **Next:** Python daemon/CLI invoke `greenfloor-engine` (or a PyO3 wrapper) instead of duplicating spend logic in `greenfloor/signing.py`. 3. **End state:** Remove duplicated Python vault spend paths; Python keeps orchestration, config, and adapters only. @@ -32,10 +32,10 @@ until local Rust offer paths reach production parity for each market flow. ## Consequences -- Feature work for vault CAT spends and offers targets `greenfloor-signer/` first. +- Feature work for vault CAT spends and offers targets `greenfloor-engine/` first. - Python parity tests validate cross-language hash/spend contracts during migration. - ADR 0002 canonical Python APIs become thin wrappers until removed. -- Coinset IO in Rust (`greenfloor-signer/src/coinset/`) is allowed for signer paths; +- Coinset IO in Rust (`greenfloor-engine/src/coinset/`) is allowed for signer paths; Python adapters remain for daemon orchestration until cutover. - Vault CAT coin selection for offers and mixed splits goes through `OfferCoinsetBackend` (live coinset adapter + simulator test backend). diff --git a/docs/decisions/0007-rust-signer-pyo3-boundary.md b/docs/decisions/0007-rust-signer-pyo3-boundary.md index 96ba9b11..fa8ec9f7 100644 --- a/docs/decisions/0007-rust-signer-pyo3-boundary.md +++ b/docs/decisions/0007-rust-signer-pyo3-boundary.md @@ -7,7 +7,7 @@ Accepted ## Decision Vault KMS signing, offer creation, bootstrap mixed-splits, and asset ID resolution run -through the in-process `greenfloor_signer` PyO3 extension backed by `greenfloor-signer`. +through the in-process `greenfloor_engine` PyO3 extension backed by `greenfloor-engine`. Configuration and vault member metadata come from `program.yaml` (`signer:` + `vault:`). On-chain IO uses `api-msp.coinset.org` via the Rust MSP coinset client. @@ -26,11 +26,11 @@ orchestration (manager, daemon, Dexie/Splash publish, ladder planning). - Rust simulator atomic-take roundtrip for CAT:CAT requested legs remains sell-CAT/request-XCH only; buy-side and cat-cat fixtures validate offer build + `create_offer_request` shape. -- Operator `greenfloor-signer create-offer` CLI parity with live Coinset is covered by PyO3 in +- Operator `greenfloor-engine create-offer` CLI parity with live Coinset is covered by PyO3 in production paths; dedicated subprocess JSON parity tests are not required while PyO3 is canonical. ## Consequences -- CI builds `greenfloor-signer-pyo3` wheel alongside `greenfloor-native`. +- CI builds `greenfloor-engine-pyo3` wheel alongside `greenfloor-native`. - Golden offer fixtures export from Rust simulator tests. - Python tests validate wiring and `validate_offer`; Rust tests validate spend semantics. diff --git a/docs/decisions/0010-rust-engine-crate-naming.md b/docs/decisions/0010-rust-engine-crate-naming.md new file mode 100644 index 00000000..1f20939e --- /dev/null +++ b/docs/decisions/0010-rust-engine-crate-naming.md @@ -0,0 +1,52 @@ +# 0010 - Rust Engine Crate Naming + +## Status + +Accepted + +Updated 2026-05-29: the Rust source directories, Cargo package, CLI binary, +Rust library target, and PyO3 module now use engine naming. + +## Context + +The Rust implementation was introduced as a signer for vault KMS paths. The crate +now also owns deterministic daemon policy: cycle orchestration, offer +reconciliation, and coin-op planning. The old "signer" name no longer describes +the scope. + +## Decision + +**Completed during this migration phase:** + +- Rename source paths to `greenfloor-engine/` and `greenfloor-engine-pyo3/`. +- Rename the Cargo package and CLI binary to `greenfloor-engine`, and the Rust + library target and PyO3 module to `greenfloor_engine`. +- `greenfloor.core.engine_bridge.import_engine()` remains the canonical Python + bridge API for in-process Rust policy; `import_signer` remains a migration alias. +- `engine_bridge` imports `greenfloor_engine`; use `engine_rebuild_hint(module=...)` + and `require_engine_method()` for operator rebuild text and stale-symbol errors. +- Group Rust policy by domain module (`cycle/`, `coin_ops/`, `offer/`, `vault/`) inside the crate. + +**Remaining compatibility:** + +- Retain `greenfloor.core.engine_bridge.import_signer` as a Python migration alias + until legacy call sites disappear. + +## Naming map + +| Layer | Current | Target | +| -------------------- | ------------------------------- | -------------------------- | +| Cargo crate | `greenfloor-engine` | done | +| PyO3 module | `greenfloor_engine` | done | +| Python bridge | `engine_bridge.import_engine()` | unchanged | +| Vault/sign path docs | "Rust signer" | "Rust engine (vault path)" | + +## Consequences + +- New Python policy surfaces use `engine_bridge`, not ad-hoc `importlib` copies. +- Adapter IO paths (`rust_signer`, `coinset`, `bls_signing`, `native_offer`, etc.) import + the engine through `engine_bridge.import_engine()`. +- Legacy module shims (`greenfloor.core.fee_budget`, `inventory`, `coin_ops_policy`) were + removed once call sites imported `greenfloor.core.coin_ops` only; coin-op policy now lives + in `_bridge.py` with `CoinOpsEngineProtocol` typing the PyO3 surface. +- ADR 0006/0007 remain valid; this ADR clarifies naming without changing boundaries. diff --git a/docs/decisions/0010-rust-kernel-crate-naming.md b/docs/decisions/0010-rust-kernel-crate-naming.md deleted file mode 100644 index 8c50420f..00000000 --- a/docs/decisions/0010-rust-kernel-crate-naming.md +++ /dev/null @@ -1,51 +0,0 @@ -# 0010 - Rust Kernel Crate Naming - -## Status - -Accepted - -## Context - -The Cargo crate `greenfloor-signer` and PyO3 module `greenfloor_signer` were introduced for -vault KMS signing. The crate now also owns deterministic daemon policy: cycle orchestration, -offer reconciliation, and coin-op planning. The name "signer" no longer describes the scope. - -## Decision - -**Near term (this migration phase):** - -- Keep crate path `greenfloor-signer/` and PyO3 module `greenfloor_signer` to avoid breaking - CI, maturin wheels, and operator installs mid-migration. -- Introduce Python `greenfloor.core.kernel_bridge.import_kernel()` as the canonical import for - in-process Rust policy; `import_signer` remains a migration alias. -- `kernel_bridge` tries `greenfloor_signer` then `greenfloor_kernel` so the PyO3 rename can - ship without touching bridge call sites; use `kernel_rebuild_hint(module=...)` and - `require_kernel_method()` for operator rebuild text and stale-symbol errors. -- Group Rust policy by domain module (`cycle/`, `coin_ops/`, `offer/`, `vault/`) inside the - crate regardless of the legacy crate name. - -**End state (post migration):** - -- Rename Cargo crate to `greenfloor-kernel`. -- Rename PyO3 module to `greenfloor_kernel`. -- Retain `greenfloor_signer` as a deprecated re-export shim for one release if needed. - -## Naming map - -| Layer | Current | Target | -| -------------------- | ------------------------------- | -------------------------- | -| Cargo crate | `greenfloor-signer` | `greenfloor-kernel` | -| PyO3 module | `greenfloor_signer` | `greenfloor_kernel` | -| Python bridge | `kernel_bridge.import_kernel()` | unchanged | -| Vault/sign path docs | "Rust signer" | "Rust kernel (vault path)" | - -## Consequences - -- New Python policy surfaces use `kernel_bridge`, not ad-hoc `importlib` copies. -- Adapter IO paths (`rust_signer`, `coinset`, `bls_signing`, `native_offer`, etc.) import - the kernel through `kernel_bridge.import_kernel()`. -- Legacy module shims (`greenfloor.core.fee_budget`, `inventory`, `coin_ops_policy`) were - removed once call sites imported `greenfloor.core.coin_ops` only; coin-op policy now lives - in `_bridge.py` with `CoinOpsKernelProtocol` typing the PyO3 surface. -- ADR 0006/0007 remain valid; this ADR clarifies naming without changing boundaries. -- Full rename is deferred until Python daemon/CLI glue migration is closer to complete. diff --git a/docs/decisions/0011-offer-request-python-import-boundaries.md b/docs/decisions/0011-offer-request-python-import-boundaries.md index 37fe953e..d1e1add0 100644 --- a/docs/decisions/0011-offer-request-python-import-boundaries.md +++ b/docs/decisions/0011-offer-request-python-import-boundaries.md @@ -6,7 +6,7 @@ Accepted (2026-05-28) ## Context -Offer-request leg math moved into `greenfloor-signer` with PyO3 bindings. Python needs stable +Offer-request leg math moved into `greenfloor-engine` with PyO3 bindings. Python needs stable import paths for runtime, daemon dispatch, vault request construction, and BLS offer building without growing `policy_bridge.py` into a flat FFI catalog. @@ -14,33 +14,33 @@ without growing `policy_bridge.py` into a flat FFI catalog. ### Canonical Rust surface -- `greenfloor-signer/src/offer/request.rs` — leg math, validation, `normalize_offer_side`, +- `greenfloor-engine/src/offer/request.rs` — leg math, validation, `normalize_offer_side`, `normalize_offer_asset_id`, `signer_split_asset_id`, `compute_signer_offer_leg_amounts`. ### Python modules (import from here, not `policy_bridge`) | Module | Use for | | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -| `greenfloor.core.offer_request_bridge` | Direct kernel access to offer-request symbols (internal bridge). | -| `greenfloor.core.offer_bootstrap_bridge` | **Stable runtime imports** — bootstrap DTOs, planner, and phase kernel wrappers. | +| `greenfloor.core.offer_request_bridge` | Direct engine access to offer-request symbols (internal bridge). | +| `greenfloor.core.offer_bootstrap_bridge` | **Stable runtime imports** — bootstrap DTOs, planner, and phase engine wrappers. | | `greenfloor.core.offer_bootstrap_policy` | Backward-compatible re-export of `offer_bootstrap_bridge` (no logic). | | `greenfloor.core.offer_policy` | **Stable runtime/daemon/BLS imports** — re-exports leg math + Dexie/publish helpers. | | `greenfloor.core.signer_offer_request` | Low-level `SignerCreateOfferRequest` / `signer_create_offer_request_from_fields` (KMS plan-dict spends only). | | `greenfloor.core.offer_action` | **Canonical offer create** — typed action request/result, pure shaping, create-phase outcome mapping. | | `greenfloor.runtime.offer_action_build` | Build action requests from `OfferBuildContext` plus local/signer runtime orchestration (asset resolution + BLS create). | -| `greenfloor.adapters.offer_action` | Kernel IO only (`build_*_offer_for_action`). | +| `greenfloor.adapters.offer_action` | Engine IO only (`build_*_offer_for_action`). | ### Offer-action create path (2026-05) - **New market-action offer creation** must use `core/offer_action` + `adapters/offer_action` (signer) or `runtime/offer_action_build` (local BLS). Do not add call sites to `rust_signer.build_vault_cat_offer` for that flow. -- Local BLS resolves ticker symbols via `resolve_action_assets_for_build_context` before kernel +- Local BLS resolves ticker symbols via `resolve_action_assets_for_build_context` before engine dispatch when ids are not already canonical. ### `policy_bridge.py` role -- Owns **pricing/publish/retry** kernel wrappers and re-exports offer-request symbols for +- Owns **pricing/publish/retry** engine wrappers and re-exports offer-request symbols for backward compatibility during migration. - **New code** should import offer-request helpers from `offer_policy` or `signer_offer_request`, not add new `policy_bridge` call sites. @@ -49,22 +49,22 @@ without growing `policy_bridge.py` into a flat FFI catalog. - `prepare_offer_build_context()` normalizes `action_side` once; `OfferBuildContext.action_side` is always `"buy"` or `"sell"`. -- `PlannedAction.side` from the cycle kernel is already `"buy"` or `"sell"`; dispatch uses - `planned_action_side()` (no kernel round-trip) instead of re-normalizing per hop. +- `PlannedAction.side` from the cycle engine is already `"buy"` or `"sell"`; dispatch uses + `planned_action_side()` (no engine round-trip) instead of re-normalizing per hop. - `normalize_offer_side()` in `offer_request_bridge` uses a fast path for common inputs and - calls the kernel only for non-standard values; parity tests lock equivalence. + calls the engine only for non-standard values; parity tests lock equivalence. ## Consequences - Next offer-migration PRs add symbols to `offer_request_py.rs` + `offer_request_bridge.py`, not `offer_build_py.rs` / `policy_bridge.py` bodies. - Bootstrap planner symbols use `offer_bootstrap_bridge.py` and `offer_bootstrap_py.rs` (not - `offer_build_py.rs`). Bridges call `kernel_bridge.bootstrap_kernel()` (`BootstrapKernelProtocol`). - Kernel API: `plan_bootstrap_mixed_outputs(ladder_entries=...)` returns `BootstrapPlanOutcome` + `offer_build_py.rs`). Bridges call `engine_bridge.bootstrap_engine()` (`BootstrapEngineProtocol`). + Engine API: `plan_bootstrap_mixed_outputs(ladder_entries=...)` returns `BootstrapPlanOutcome` (`ready` / `needs_split` / `cannot_fund` / `invalid_ladder` / `invalid_coins`). -- Rust layout: `greenfloor-signer/src/offer/bootstrap/planner.rs` (deficit planner), +- Rust layout: `greenfloor-engine/src/offer/bootstrap/planner.rs` (deficit planner), `offer/bootstrap/phase.rs` (early/executed phase snapshots). PyO3 marshalling: - `greenfloor-signer-pyo3/src/py_utils/bootstrap_marshal.rs`. + `greenfloor-engine-pyo3/src/py_utils/bootstrap_marshal.rs`. - Runtime orchestration lives in `greenfloor/runtime/offer_bootstrap.py` (`BootstrapRuntimeDeps`, `BootstrapPreflight`, `BootstrapSplitExecution`). Phase DTOs live in `greenfloor.offer_bootstrap`; early/executed phase mapping is Rust via `offer_bootstrap_bridge.py`. diff --git a/docs/plan.md b/docs/plan.md index f0f96701..483f139f 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -152,12 +152,12 @@ These are the only priorities. Do not start new feature work until G1-G3 are com - `tests/test_chia_wallet_sdk_simulator_harness.py` was deleted (2026-02-25): all six tests ran `cargo test` on `chia-sdk-driver` Rust internals (CAT issuance, catalog, reward distributor) - and tested no GreenFloor code. SDK has its own CI. `tests/test_greenfloor_signer_integration.py` + and tested no GreenFloor code. SDK has its own CI. `tests/test_greenfloor_engine_integration.py` covers the signer surface GreenFloor actually uses. - CI pytest runs as a standalone step (`"Test suite (pytest)"`) with `-v --tb=short`, giving each test its own line in logs and a dedicated collapsible section with pass/fail badge. The `pre-commit` step skips pytest via `SKIP: pytest` so lint/type-check and tests have independent status indicators. Locally, `pre-commit run --all-files` still runs pytest as part of the hook set. - Three tests are intentionally skipped in CI: - `test_replay_captured_cat_parse_cases` — requires `GREENFLOOR_CAT_PARSE_REPLAY_CASES_DIR` (operator-provided fixture directory). - - `test_greenfloor_signer_validate_offer_rejects_garbage` and `test_greenfloor_signer_from_input_spend_bundle_xch_round_trip_offer` in `tests/test_greenfloor_signer_integration.py` — require `GREENFLOOR_RUN_SIGNER_INTEGRATION_TESTS=1` (compiled `greenfloor_signer` + `chia-wallet-sdk` bindings). + - `test_greenfloor_engine_validate_offer_rejects_garbage` and `test_greenfloor_engine_from_input_spend_bundle_xch_round_trip_offer` in `tests/test_greenfloor_engine_integration.py` — require `GREENFLOOR_RUN_ENGINE_INTEGRATION_TESTS=1` (compiled `greenfloor_engine` + `chia-wallet-sdk` bindings). ## Deferred Backlog (Post-Testnet Proof) diff --git a/docs/progress.md b/docs/progress.md index e74e16a6..856aa863 100644 --- a/docs/progress.md +++ b/docs/progress.md @@ -1,38 +1,44 @@ # Progress Log +## 2026-05-29 (ADR 0010 Rust engine rename) + +- **Source layout:** Rust crates now live under `greenfloor-engine/` and `greenfloor-engine-pyo3/`. +- **Cargo/PyO3 names:** Cargo package and CLI binary are `greenfloor-engine`; Rust library target and PyO3 module are `greenfloor_engine`. +- **Repo references:** CI, maturin wheel caching, engine rebuild hints, integration tests, and docs now point at the engine paths/names. + ## 2026-05-28 (ADR 0010 rename-prep hygiene) -- **`greenfloor/core/kernel_bridge.py`:** `KERNEL_MODULE_LEGACY` / `KERNEL_MODULE_TARGET` constants; `import_kernel()` tries legacy then target module; `require_kernel_method()` + `kernel_rebuild_hint(module=...)` for stale-wheel operator text; typed kernel views share one `import_kernel()` load. -- **Policy bridges:** `policy_bridge`, `offer_bootstrap_bridge`, and `offer_request_bridge` delegate rebuild hints to `kernel_bridge` (no duplicated maturin strings). -- **Rust docs:** crate-level `lib.rs` doc + `cycle/` / `offer/` module headers; `greenfloor-signer` Cargo description updated; PyO3 `lib.rs` module comment points callers at `kernel_bridge`. -- **Adapter docstrings:** `rust_signer`, `native_offer`, `bls_signing`, `bls_cat_coins`, `coinset` use "Rust kernel" wording; stable error-code prefixes unchanged. -- **Tests:** `tests/test_kernel_bridge.py` covers alias, rebuild hint, and import candidate order. +- **`greenfloor/core/engine_bridge.py`:** `ENGINE_MODULE_LEGACY` / `ENGINE_MODULE_TARGET` constants; `import_engine()` tries legacy then target module; `require_engine_method()` + `engine_rebuild_hint(module=...)` for stale-wheel operator text; typed engine views share one `import_engine()` load. +- **Policy bridges:** `policy_bridge`, `offer_bootstrap_bridge`, and `offer_request_bridge` delegate rebuild hints to `engine_bridge` (no duplicated maturin strings). +- **Rust docs:** crate-level `lib.rs` doc + `cycle/` / `offer/` module headers; `greenfloor-engine` Cargo description updated; PyO3 `lib.rs` module comment points callers at `engine_bridge`. +- **Adapter docstrings:** `rust_signer`, `native_offer`, `bls_signing`, `bls_cat_coins`, `coinset` use "Rust engine" wording; stable error-code prefixes unchanged. +- **Tests:** `tests/test_engine_bridge.py` covers alias, rebuild hint, and import candidate order. -## 2026-05-28 (Coin-op effective counts — Rust kernel by domain) +## 2026-05-28 (Coin-op effective counts — Rust engine by domain) -- **`greenfloor-signer/src/coin_ops/effective_counts.rs`:** `effective_sell_bucket_counts_for_coin_ops` takes `LadderTargetRow` slices (size + target only); merges wallet counts with live and in-cycle sell offers toward ladder targets. -- **`greenfloor-signer/src/cycle/strategy_action.rs`:** `executed_sell_offer_counts_by_size` aggregates executed sell actions; Rust reads Python `counts_as_executed` (single owner for status semantics in `StrategyActionItem`). +- **`greenfloor-engine/src/coin_ops/effective_counts.rs`:** `effective_sell_bucket_counts_for_coin_ops` takes `LadderTargetRow` slices (size + target only); merges wallet counts with live and in-cycle sell offers toward ladder targets. +- **`greenfloor-engine/src/cycle/strategy_action.rs`:** `executed_sell_offer_counts_by_size` aggregates executed sell actions; Rust reads Python `counts_as_executed` (single owner for status semantics in `StrategyActionItem`). - **PyO3:** `coin_ops_py.rs` + `ladder_target_rows_from_py_list`; `cycle/strategy_counts_py.rs` + `strategy_action_sell_counts_from_py_list`; shared `require_i64_attr` in `py_utils/common.rs`. -- **Python bridges:** `core/coin_ops/_bridge.py`, `core/cycle/_bridge_orchestration.py`, `core/kernel_maps.py` (`require_i64_i64_map`, `require_side_offer_count_maps`); daemon imports canonical core helpers directly (no pass-through wrappers). +- **Python bridges:** `core/coin_ops/_bridge.py`, `core/cycle/_bridge_orchestration.py`, `core/engine_maps.py` (`require_i64_i64_map`, `require_side_offer_count_maps`); daemon imports canonical core helpers directly (no pass-through wrappers). - **Tests:** `tests/test_coin_ops_policy_parity.py`, `tests/test_cycle_strategy_action_parity.py`; removed duplicate gates from `tests/test_daemon_strategy_integration.py`. -## 2026-05-28 (Offer request leg math — Rust kernel + PyO3) +## 2026-05-28 (Offer request leg math — Rust engine + PyO3) -- **`greenfloor-signer/src/offer/request.rs`:** canonical leg math — `quote_mojos_for_base_size`, `signer_split_asset_id`, `compute_signer_offer_leg_amounts` (normalized asset ids), `normalize_offer_asset_id`; Rust-only `normalize_offer_side`. +- **`greenfloor-engine/src/offer/request.rs`:** canonical leg math — `quote_mojos_for_base_size`, `signer_split_asset_id`, `compute_signer_offer_leg_amounts` (normalized asset ids), `normalize_offer_asset_id`; Rust-only `normalize_offer_side`. - **PyO3:** `offer_request_py.rs` exposes leg-math symbols; `offer_build_py.rs` keeps pricing/publish helpers; shared `pricing_dict_from_py` in `py_utils/common.rs`. -- **Python bridges:** `offer_request_bridge.py` for kernel leg math; `signer_offer_request.py` for dataclasses + `build_signer_create_offer_request`; `normalize_offer_side` and asset helpers delegate to Rust (stable import via `offer_policy`). Removed `core/offer_side.py`. +- **Python bridges:** `offer_request_bridge.py` for engine leg math; `signer_offer_request.py` for dataclasses + `build_signer_create_offer_request`; `normalize_offer_side` and asset helpers delegate to Rust (stable import via `offer_policy`). Removed `core/offer_side.py`. - **Tests:** Rust unit tests in `request.rs`; golden + validation in `tests/test_signer_create_offer_parity.py`; BLS input contracts in `tests/test_offer_builder_sdk.py`. -- **Follow-up:** `docs/decisions/0011-offer-request-python-import-boundaries.md`; `OfferBuildContext.action_side` cached at prepare; daemon dispatch uses `planned_action_side()` (no per-hop kernel); `normalize_offer_side` fast path in `offer_request_bridge`. +- **Follow-up:** `docs/decisions/0011-offer-request-python-import-boundaries.md`; `OfferBuildContext.action_side` cached at prepare; daemon dispatch uses `planned_action_side()` (no per-hop engine); `normalize_offer_side` fast path in `offer_request_bridge`. ## 2026-05-28 (Signer fixture hygiene follow-up — consolidate protocols and tests) -- **Kernel protocols:** re-collapsed domain splits back into single `greenfloor/core/kernel_protocol.py` (five-file split removed). +- **Engine protocols:** re-collapsed domain splits back into single `greenfloor/core/engine_protocol.py` (five-file split removed). - **Offer side + request:** signer request builder in `core/signer_offer_request.py` (uses `mojo_multiplier_for_le`); side normalization later moved to Rust (`offer/request.rs`) via `offer_request_bridge` / `offer_policy`. -- **Dexie visibility asset checks:** moved offered/requested asset-match validation from `runtime/offer_publish.py` into `greenfloor-signer/src/offer/publish.rs`; PyO3 exposes `dexie_offer_asset_expectation_error` and Python runtime delegates via `core.offer_policy`. -- **Bootstrap mixed-output planner:** `plan_bootstrap_mixed_outputs` in `greenfloor-signer/src/offer/bootstrap/` (`planner.rs` + `phase.rs`) returns `BootstrapPlanOutcome`; PyO3 marshalling in `py_utils/bootstrap_marshal.rs`; stable Python imports via `core.offer_bootstrap_bridge`; fee eligibility stays in `runtime/offer_bootstrap.py`. -- **Bootstrap block gate:** moved `bootstrap_blocks_offer` decision shaping into `greenfloor-signer/src/offer/publish.rs` (`bootstrap_block_error`); Python orchestration delegates via `core.offer_policy`. -- **Publish asset-field shaping:** moved `expected_publish_asset_fields` mapping into `greenfloor-signer/src/offer/publish.rs`; orchestration now calls `core.offer_policy.expected_publish_asset_fields` directly. -- **Offer policy bridge contract:** removed silent Python fallbacks for migrated offer policy helpers; bridge now requires kernel symbols and fails fast with rebuild guidance when extensions are stale. +- **Dexie visibility asset checks:** moved offered/requested asset-match validation from `runtime/offer_publish.py` into `greenfloor-engine/src/offer/publish.rs`; PyO3 exposes `dexie_offer_asset_expectation_error` and Python runtime delegates via `core.offer_policy`. +- **Bootstrap mixed-output planner:** `plan_bootstrap_mixed_outputs` in `greenfloor-engine/src/offer/bootstrap/` (`planner.rs` + `phase.rs`) returns `BootstrapPlanOutcome`; PyO3 marshalling in `py_utils/bootstrap_marshal.rs`; stable Python imports via `core.offer_bootstrap_bridge`; fee eligibility stays in `runtime/offer_bootstrap.py`. +- **Bootstrap block gate:** moved `bootstrap_blocks_offer` decision shaping into `greenfloor-engine/src/offer/publish.rs` (`bootstrap_block_error`); Python orchestration delegates via `core.offer_policy`. +- **Publish asset-field shaping:** moved `expected_publish_asset_fields` mapping into `greenfloor-engine/src/offer/publish.rs`; orchestration now calls `core.offer_policy.expected_publish_asset_fields` directly. +- **Offer policy bridge contract:** removed silent Python fallbacks for migrated offer policy helpers; bridge now requires engine symbols and fails fast with rebuild guidance when extensions are stale. - **CLI stale-symbol UX:** `runtime/offer_orchestration.py` now catches offer-policy bridge exceptions and emits structured `offer_policy_error:*` result payloads; `test_build_and_post_offer_surfaces_stale_kernel_symbol_as_user_error` proves the manager CLI path returns a user-facing failure string. - **Rust tests:** `offer_leg_scenarios_build_on_simulator` (renamed from roundtrip; build-only on sim for CAT:CAT legs). - **Python tests:** `test_signer_golden_fixture_contract` (schema + core parity + validate in one parametrized test); `test_offer_runtime.py` covers signer IO shell only (leg math owned by parity fixtures). @@ -40,21 +46,21 @@ ## 2026-05-28 (Rust migration items 2–3 — signer fixtures, bridge/protocol hygiene, docs) -- **Golden fixtures:** `buy_side.json`, `cat_cat.json`, and roundtrip scenarios exported from simulator with embedded `create_offer_request` and `runtime_parity`; regenerate via `EXPORT_SIGNER_FIXTURES=1 cargo test export_signer_fixtures_to_disk` in `greenfloor-signer/`. +- **Golden fixtures:** `buy_side.json`, `cat_cat.json`, and roundtrip scenarios exported from simulator with embedded `create_offer_request` and `runtime_parity`; regenerate via `EXPORT_SIGNER_FIXTURES=1 cargo test export_signer_fixtures_to_disk` in `greenfloor-engine/`. - **Rust tests:** `build_vault_cat_offer_roundtrips`, `offer_leg_scenarios_build_on_simulator`; shared `setup_roundtrip` / `build_offer_from_setup`; `fund_vault_two_cats()` for dual-CAT issuance. -- **Python tests:** `tests/test_signer_create_offer_parity.py`; `tests/test_greenfloor_signer_integration.py` (config roundtrip + gated PyO3 integration). -- **Kernel protocols:** composed in `kernel_protocol.py` (cycle, cancel, notification, offer, retry). +- **Python tests:** `tests/test_signer_create_offer_parity.py`; `tests/test_greenfloor_engine_integration.py` (config roundtrip + gated PyO3 integration). +- **Engine protocols:** composed in `engine_protocol.py` (cycle, cancel, notification, offer, retry). - **Cycle bridge:** `_bridge_managed.py`, `_bridge_orchestration.py`, `_bridge_common.py`; no `_bridge.py` shim. - **Docs:** `docs/plan.md`, `docs/runbook.md`, ADR 0007 deferred notes updated for signer-only vault path. ## 2026-05-28 (Rust migration steps 14–16 — offer validate, build context, retry, coin-op gates) -- **Step 14 — Retry policy (`greenfloor-signer/src/cycle/retry.rs`):** `parse_rate_limit_retry_seconds`, Dexie invalid-offer retry gating/sleep, Coinset fee lookup sleep; PyO3 `retry_py.rs`; Python `greenfloor/core/retry_policy.py` + `moderate_retry.py` / `coinset_runtime.py` wired to kernel. -- **Step 15 — Offer build context (`greenfloor-signer/src/offer/build_context.rs`):** `resolve_offer_expiry_for_pricing`, `resolve_quote_price_for_pricing`, `mojo_multiplier_for_leg`; PyO3 `offer_build_py.rs`; Python `greenfloor/core/offer_policy.py`; `offer_build_context.py` delegates. +- **Step 14 — Retry policy (`greenfloor-engine/src/cycle/retry.rs`):** `parse_rate_limit_retry_seconds`, Dexie invalid-offer retry gating/sleep, Coinset fee lookup sleep; PyO3 `retry_py.rs`; Python `greenfloor/core/retry_policy.py` + `moderate_retry.py` / `coinset_runtime.py` wired to engine. +- **Step 15 — Offer build context (`greenfloor-engine/src/offer/build_context.rs`):** `resolve_offer_expiry_for_pricing`, `resolve_quote_price_for_pricing`, `mojo_multiplier_for_leg`; PyO3 `offer_build_py.rs`; Python `greenfloor/core/offer_policy.py`; `offer_build_context.py` delegates. - **Step 14 (offer validate) — `validate_offer_text` / `verify_offer_for_dexie`:** extended in `offer/codec.rs` with typed `SignerError` variants, single-decode validation, and stable Dexie error codes; callers use `offer_policy.verify_offer_for_dexie` (ImportError mapped there). -- **Step 16 — Coin-op gates (`greenfloor-signer/src/coin_ops/gate.rs`, `wallet_coin.rs`):** `evaluate_coin_split_gate`, `evaluate_coin_combine_gate`, `coin_op_should_stop`, `is_spendable_wallet_coin`; PyO3 returns `SplitDenominationReadiness` / `CombineDenominationReadiness`; `runtime/coin_ops/readiness.py` owns iteration payload + target-based evaluation; dict JSON via `to_payload()` only. -- **Policy bridge:** `greenfloor/core/policy_bridge.py` (offer + retry); stable paths `offer_policy.py` / `retry_policy.py`; `policy_kernel()` / `coin_ops_kernel()` on `PolicyKernelProtocol`. -- **Tests:** `tests/test_retry_policy_parity.py`, `tests/test_coin_ops_gate_parity.py`; `test_offer_publish.py` rewritten for kernel path; `tests/helpers/kernel_mock.MinimalSignerKernel` for partial signer stubs. +- **Step 16 — Coin-op gates (`greenfloor-engine/src/coin_ops/gate.rs`, `wallet_coin.rs`):** `evaluate_coin_split_gate`, `evaluate_coin_combine_gate`, `coin_op_should_stop`, `is_spendable_wallet_coin`; PyO3 returns `SplitDenominationReadiness` / `CombineDenominationReadiness`; `runtime/coin_ops/readiness.py` owns iteration payload + target-based evaluation; dict JSON via `to_payload()` only. +- **Policy bridge:** `greenfloor/core/policy_bridge.py` (offer + retry); stable paths `offer_policy.py` / `retry_policy.py`; `policy_engine()` / `coin_ops_engine()` on `PolicyEngineProtocol`. +- **Tests:** `tests/test_retry_policy_parity.py`, `tests/test_coin_ops_gate_parity.py`; `test_offer_publish.py` rewritten for engine path; `tests/helpers/engine_mock.MinimalSignerEngine` for partial signer stubs. ### Agent handoff — next phase (Later / structural) @@ -64,34 +70,34 @@ Steps 1–16 moved deterministic policy out of large Python modules. **Do not** | -------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | L1 | `storage/sqlite.py` (~690) | Defer until daemon IO contract stabilizes; not on hot offer-post path | | L2 | `runtime/coin_ops/daemon_execution.py` (~585) | Keep Coinset/wallet IO in Python; only move more polling tables if needed | -| L3 | `daemon/` + `cli/` (~8k) | Rust binary for `greenfloord` / `greenfloor-manager` subcommands calling `greenfloor-kernel` | -| L4 | ADR 0010 rename | `greenfloor-signer` → `greenfloor-kernel`, `greenfloor_signer` shim one release | +| L3 | `daemon/` + `cli/` (~8k) | Rust binary for `greenfloord` / `greenfloor-manager` subcommands calling `greenfloor-engine` | +| L4 | ADR 0010 rename | Done for source directories, Cargo package, CLI binary, Rust library, and PyO3 module | | L5 | `config/` | Keep YAML parse in Python unless Rust config loader reaches parity with `config/models.py` validation | | L6 | Combine-until-ready gate | Done: `evaluate_coin_combine_gate` in `coin_ops/gate.rs` (step 16 follow-up) | -**CI parity gates for Later steps:** integration tests against live `greenfloor_signer` wheel; daemon soak on mainnet canary market. +**CI parity gates for Later steps:** integration tests against live `greenfloor_engine` wheel; daemon soak on mainnet canary market. ## 2026-05-28 (Rust hex utilities + low-inventory alerts — step 12) -- **`greenfloor-signer/src/hex.rs`:** signer-boundary hex helpers (`is_hex_id`, `normalize_hex_id`, `default_mojo_multiplier_for_asset`). -- **`greenfloor-signer/src/cycle/notifications.rs`:** low-inventory threshold resolution, hysteresis clear, dedup cooldown, and alert event shaping. -- **PyO3:** `hex_py.rs` + `notifications_py.rs`; `greenfloor/hex_utils.py` is pure Python for CLI/daemon hot paths; `greenfloor/core/notifications.py` is a typed `policy_kernel()` bridge. -- **`DeterministicPolicyKernelProtocol`:** composed cycle/cancel/notification protocol in `greenfloor/core/kernel_protocol.py`; `_bridge.py` calls `policy_kernel()` directly. -- **Parity:** `tests/test_coin_ops_policy_parity.py` gates `canonical_is_xch`, `normalize_hex_id`, `is_hex_id`, and `default_mojo_multiplier_for_asset` against the kernel; `tests/test_low_inventory_alerts.py` remains the notification parity gate. +- **`greenfloor-engine/src/hex.rs`:** signer-boundary hex helpers (`is_hex_id`, `normalize_hex_id`, `default_mojo_multiplier_for_asset`). +- **`greenfloor-engine/src/cycle/notifications.rs`:** low-inventory threshold resolution, hysteresis clear, dedup cooldown, and alert event shaping. +- **PyO3:** `hex_py.rs` + `notifications_py.rs`; `greenfloor/hex_utils.py` is pure Python for CLI/daemon hot paths; `greenfloor/core/notifications.py` is a typed `policy_engine()` bridge. +- **`DeterministicPolicyEngineProtocol`:** composed cycle/cancel/notification protocol in `greenfloor/core/engine_protocol.py`; `_bridge.py` calls `policy_engine()` directly. +- **Parity:** `tests/test_coin_ops_policy_parity.py` gates `canonical_is_xch`, `normalize_hex_id`, `is_hex_id`, and `default_mojo_multiplier_for_asset` against the engine; `tests/test_low_inventory_alerts.py` remains the notification parity gate. - **Migration status:** step 12 complete for shared hex helpers and v1 notification policy. -## 2026-05-28 (Rust cancel-policy decision kernel — step 13) +## 2026-05-28 (Rust cancel-policy decision engine — step 13) -- **`greenfloor-signer/src/cycle/cancel.rs`:** `abs_move_bps`, threshold resolution, eligibility/trigger decision, and open-offer ID selection (Dexie status `0`). +- **`greenfloor-engine/src/cycle/cancel.rs`:** `abs_move_bps`, threshold resolution, eligibility/trigger decision, and open-offer ID selection (Dexie status `0`). - **PyO3 + core surface:** `cycle/cancel_py.rs`; policy in `greenfloor/core/cancel_policy.py` with typed `OpenOfferRow` and `CancelPolicyDecision`; daemon `cancel_policy.py` keeps Dexie cancel IO, cooldown, and SQLite persistence only (~110 lines). - **`MarketConfig.cancel_move_threshold_bps`:** parsed once at config load (removed from runtime `pricing` dict); env override via `unstable_cancel_move_threshold_bps_from_env()`. - **`market_helpers.py`:** `_abs_move_bps` and `_cancel_move_threshold_bps` delegate to core using typed market/env thresholds. -- **Tests:** Rust unit tests in `cancel.rs`; `tests/test_cancel_policy_kernel.py` + existing `tests/test_daemon_cancel_policy.py` remain parity gates. -- **Migration status:** step 13 complete for cancel-policy decision kernel. +- **Tests:** Rust unit tests in `cancel.rs`; `tests/test_cancel_policy_engine.py` + existing `tests/test_daemon_cancel_policy.py` remain parity gates. +- **Migration status:** step 13 complete for cancel-policy decision engine. ### Agent handoff — next migration steps (post step 13) -Steps 1–13 moved deterministic daemon/coin-op/shared policy into `greenfloor-signer`. Remaining large Python modules are mostly **IO adapters** (SQLite, Dexie, Coinset, websocket, CLI). Do not migrate them wholesale; extract **pure decision** hunks first (same pattern as steps 9–13). +Steps 1–13 moved deterministic daemon/coin-op/shared policy into `greenfloor-engine`. Remaining large Python modules are mostly **IO adapters** (SQLite, Dexie, Coinset, websocket, CLI). Do not migrate them wholesale; extract **pure decision** hunks first (same pattern as steps 9–13). **Largest tracked Python modules (line counts approximate, 2026-05-28):** @@ -103,13 +109,13 @@ Steps 1–13 moved deterministic daemon/coin-op/shared policy into `greenfloor-s | `greenfloor/runtime/offer_publish.py` | ~489 | Venue publish helpers | IO; audit any retry/classify helpers already in Rust | | `greenfloor/daemon/watchlist.py` | ~486 | Websocket watchlist | **Keep Python** — network IO | | `greenfloor/runtime/offer_orchestration.py` | ~485 | Bootstrap → create → verify → publish | IO orchestration; split only if a pure planning hunk emerges | -| `greenfloor/runtime/offer_runtime.py` | ~484 | Vault KMS offer build/post runtime | IO; signing canonical path is `greenfloor-signer` | -| `greenfloor/core/cycle/_bridge.py` | ~440 | Cycle PyO3 bridge | **Hygiene only** — typed via `DeterministicPolicyKernelProtocol`; shrink by moving wrappers to `policy.py` | +| `greenfloor/runtime/offer_runtime.py` | ~484 | Vault KMS offer build/post runtime | IO; signing canonical path is `greenfloor-engine` | +| `greenfloor/core/cycle/_bridge.py` | ~440 | Cycle PyO3 bridge | **Hygiene only** — typed via `DeterministicPolicyEngineProtocol`; shrink by moving wrappers to `policy.py` | | `greenfloor/adapters/coinset.py` | ~440 | Coinset HTTP adapter | **Keep Python** — adapter boundary | **Recommended step 14 (next pure-policy target):** **`moderate_retry.py` + offer publish transient classification** — `parse_rate_limit_retry_seconds`, exponential backoff loop inputs, and any remaining string-matching retry gates in `runtime/offer_publish.py` that duplicate Rust managed-path classifiers. Small surface (~80 lines), high reuse across Dexie/Coinset paths. -**Recommended step 15:** **`offer_build_context.py` pricing/expiry shaping** — extract deterministic `OfferBuildContext` validation and expiry derivation (if any logic remains outside Rust `expiry_seconds_for_action`) into `greenfloor-signer/src/offer/`; Python keeps YAML/config IO only. +**Recommended step 15:** **`offer_build_context.py` pricing/expiry shaping** — extract deterministic `OfferBuildContext` validation and expiry derivation (if any logic remains outside Rust `expiry_seconds_for_action`) into `greenfloor-engine/src/offer/`; Python keeps YAML/config IO only. **Recommended step 16 (optional, larger):** **`runtime/coin_ops/steps.py` + `daemon_execution.py` iteration skeleton** — only after steps 14–15; move step-gating / skip-reason tables to Rust; Python retains spend-bundle broadcast and Coinset polling. @@ -120,7 +126,7 @@ Steps 1–13 moved deterministic daemon/coin-op/shared policy into `greenfloor-s - `daemon/offer_dispatch/*` — already consolidated IO; do not re-split without a Rust policy hunk to absorb. - Submodule / SDK trees (`chia-wallet-sdk/`) — out of repo migration scope. -**CI parity gates for new steps:** Rust unit tests in the new `*.rs` module; Python parity test file or extension of an existing kernel test; run `pre-commit run --all-files` (with `SKIP=pytest` for lint-only pass) before PR. +**CI parity gates for new steps:** Rust unit tests in the new `*.rs` module; Python parity test file or extension of an existing engine test; run `pre-commit run --all-files` (with `SKIP=pytest` for lint-only pass) before PR. ## 2026-05-27 (strategy_dispatch exit criteria — package shrink) @@ -133,21 +139,21 @@ Steps 1–13 moved deterministic daemon/coin-op/shared policy into `greenfloor-s ## 2026-05-27 (Rust coin-op selection/planning — step 11) -- **`greenfloor-signer/src/coin_ops/selection.rs`:** spendable coin subset-sum selection, largest/exact pickers, and sub-CAT change dust guard. -- **`greenfloor-signer/src/coin_ops/split_planning.rs`:** `plan_auto_split_selection` (CLI vs daemon profiles), combine-prereq gating, and `plan_auto_combine_inputs`. -- **PyO3 + core surface:** `coin_ops_py.rs` + `py_utils.rs` expose typed `SplitCoinPlan` / `SplitCombinePrereqPlan` / `SplitSkipPlan` via `greenfloor.core.coin_ops.types`; bridge in `_bridge.py` with `CoinOpsKernelProtocol` extended. +- **`greenfloor-engine/src/coin_ops/selection.rs`:** spendable coin subset-sum selection, largest/exact pickers, and sub-CAT change dust guard. +- **`greenfloor-engine/src/coin_ops/split_planning.rs`:** `plan_auto_split_selection` (CLI vs daemon profiles), combine-prereq gating, and `plan_auto_combine_inputs`. +- **PyO3 + core surface:** `coin_ops_py.rs` + `py_utils.rs` expose typed `SplitCoinPlan` / `SplitCombinePrereqPlan` / `SplitSkipPlan` via `greenfloor.core.coin_ops.types`; bridge in `_bridge.py` with `CoinOpsEngineProtocol` extended. - **Python IO glue:** `runtime/coin_ops/planning.py` and `selection.py` are thin re-exports (~80 lines total); CLI/daemon execution modules unchanged. - **Tests:** Rust unit tests in `selection.rs` and `split_planning.rs`; existing `tests/test_coin_ops_planning.py` and daemon parallel selection tests remain parity gates. - **Migration status:** step 11 complete for coin-op selection/planning helpers. -- **Deferred hygiene (superseded):** ~~split `kernel_protocol.py` by domain~~ — consolidated back into one module after signer-fixture work (2026-05-28). +- **Deferred hygiene (superseded):** ~~split `engine_protocol.py` by domain~~ — consolidated back into one module after signer-fixture work (2026-05-28). -## 2026-05-27 (Rust coin-op policy kernel — step 10) +## 2026-05-27 (Rust coin-op policy engine — step 10) -- **`greenfloor-signer/src/coin_ops/`:** deterministic coin-op policy bundle — `plan_coin_ops`, fee-budget partitioning, bucket counting, and CAT min-amount guard. -- **PyO3 + core surface:** `coin_ops_py.rs` exposes typed `BucketSpec` / `CoinOpPlan` round-trip via `greenfloor.core.coin_ops` dataclasses; Python policy is consolidated in `greenfloor/core/coin_ops/_bridge.py` with `CoinOpsKernelProtocol` typing. +- **`greenfloor-engine/src/coin_ops/`:** deterministic coin-op policy bundle — `plan_coin_ops`, fee-budget partitioning, bucket counting, and CAT min-amount guard. +- **PyO3 + core surface:** `coin_ops_py.rs` exposes typed `BucketSpec` / `CoinOpPlan` round-trip via `greenfloor.core.coin_ops` dataclasses; Python policy is consolidated in `greenfloor/core/coin_ops/_bridge.py` with `CoinOpsEngineProtocol` typing. - **Python IO glue:** `runtime/coin_ops/` and `daemon/coin_ops_cycle.py` unchanged — still own Coinset/wallet execution, fee lookup, and SQLite budget accounting. - **Tests:** Rust unit tests in `coin_ops/{plan,fee_budget,inventory,policy}.rs`; Python parity/FFI contract in `tests/test_coin_ops_policy_parity.py`; existing planner/fee/inventory tests remain parity gates. -- **Follow-up:** consolidated Python surface into `greenfloor/core/coin_ops/` package + shared `kernel_bridge`; deduplicated PyO3 class caching; `amount_meets_coin_op_min_mojos` is the single Rust threshold check (ADR 0010). +- **Follow-up:** consolidated Python surface into `greenfloor/core/coin_ops/` package + shared `engine_bridge`; deduplicated PyO3 class caching; `amount_meets_coin_op_min_mojos` is the single Rust threshold check (ADR 0010). - **Migration status:** step 10 complete for core coin-op policy. - **Completed (step 11):** coin-op selection/planning — see step 11 entry above (`selection.rs`, `split_planning.rs`). @@ -155,28 +161,28 @@ Steps 1–13 moved deterministic daemon/coin-op/shared policy into `greenfloor-s Record these for the next migration agent; none are merge blockers for step 12. -1. **`DeterministicPolicyKernelProtocol`:** ✅ composed in step 12/13 follow-up (`greenfloor/core/kernel_protocol.py`). -2. **Dual XCH / hex asset identity (Python + Rust):** ✅ `hex_utils.py` pure Python for CLI paths; kernel `hex.rs` at signer boundary; parity gated in `tests/test_coin_ops_policy_parity.py`. -3. **`greenfloor-signer-pyo3/src/py_utils.rs` (~420 lines):** split into domain submodules (for example `py_utils/coin_ops.rs`, `py_utils/cycle.rs`) before the file reaches ~500 lines. ✅ split done in step 11 follow-up. +1. **`DeterministicPolicyEngineProtocol`:** ✅ composed in step 12/13 follow-up (`greenfloor/core/engine_protocol.py`). +2. **Dual XCH / hex asset identity (Python + Rust):** ✅ `hex_utils.py` pure Python for CLI paths; engine `hex.rs` at signer boundary; parity gated in `tests/test_coin_ops_policy_parity.py`. +3. **`greenfloor-engine-pyo3/src/py_utils.rs` (~420 lines):** split into domain submodules (for example `py_utils/coin_ops.rs`, `py_utils/cycle.rs`) before the file reaches ~500 lines. ✅ split done in step 11 follow-up. 4. **`strategy_dispatch/` line-count exit (~400 target):** ✅ met (386 lines); execution IO in `offer_dispatch/`. -## 2026-05-27 (Rust offer reconciliation kernel — step 9) +## 2026-05-27 (Rust offer reconciliation engine — step 9) -- **`greenfloor-signer/src/cycle/reconcile.rs`:** Coinset-first watched-offer transition kernel — Dexie status fallback, missing-offer (404) handling, taker field shaping, and `CycleOfferTransition` outputs. +- **`greenfloor-engine/src/cycle/reconcile.rs`:** Coinset-first watched-offer transition engine — Dexie status fallback, missing-offer (404) handling, taker field shaping, and `CycleOfferTransition` outputs. - **PyO3 + core surface:** typed `CycleOfferTransition` via `reconcile_py.rs` + `py_utils.rs`; policy in `greenfloor/core/offer_reconcile/`. - **Python IO glue:** `greenfloor/runtime/offer_reconciliation.py` keeps Dexie fetch, SQLite tx-signal lookup (`_coinset_signal_lists`), audit persistence, and batch reconcile loops only. - **Tests:** Rust unit tests in `reconcile.rs`; Python wiring in `tests/test_offer_reconcile_kernel.py`; existing manager/daemon reconcile integration tests remain parity gates. - **Migration status:** step 9 complete for offer lifecycle reconciliation policy. -- **Completed (step 10):** core coin-op policy bundle — see step 10 entry above (`greenfloor-signer/src/coin_ops/`, `greenfloor/core/coin_ops/`). +- **Completed (step 10):** core coin-op policy bundle — see step 10 entry above (`greenfloor-engine/src/coin_ops/`, `greenfloor/core/coin_ops/`). -## 2026-05-27 (Rust cycle kernel step 8 — reseed gap planning) +## 2026-05-27 (Rust cycle engine step 8 — reseed gap planning) -- **`plan_reseed_actions_from_gap` in Rust:** `greenfloor-signer/src/cycle/reseed.rs` — offer-size-gap reseed (typed skip reasons, per-size repeat from active vs target counts, `missing_by_size`; seed templates from internal `evaluate_market` on empty bucket state). +- **`plan_reseed_actions_from_gap` in Rust:** `greenfloor-engine/src/cycle/reseed.rs` — offer-size-gap reseed (typed skip reasons, per-size repeat from active vs target counts, `missing_by_size`; seed templates from internal `evaluate_market` on empty bucket state). - **PyO3 + core surface:** typed `ReseedGapPlan` / `ReseedSkipReason` via `strategy_py.rs` + `py_utils.rs`; policy in `greenfloor/core/cycle/policy.py`; types in `greenfloor/core/cycle_reseed.py` (exported from `greenfloor.core.cycle`). -- **Label parity:** `reseed_skip_reason_labels()` in Rust; `tests/test_cycle_reseed.py` asserts Python `ReseedSkipReason` values match the kernel. +- **Label parity:** `reseed_skip_reason_labels()` in Rust; `tests/test_cycle_reseed.py` asserts Python `ReseedSkipReason` values match the engine. - **Python IO glue:** `greenfloor/daemon/strategy_reseed.py` — SQLite active-count fetch + structured audit logging only (~130 lines). - **Removed:** `greenfloor/core/reseed.py`, `evaluate_reseed_candidates` shim in `strategy_state.py`. -- **Repo hygiene:** `.gitignore` adds `greenfloor-signer/target/` and `greenfloor-signer-pyo3/target/`. +- **Repo hygiene:** `.gitignore` adds `greenfloor-engine/target/` and `greenfloor-engine-pyo3/target/`. - **Migration status:** step 8 complete for sell-only gap reseed; `strategy_dispatch/` shrink and `strategy_config_from_market` extraction remain toward ~400-line exit criteria. ## 2026-05-27 (Review follow-up — enum outcomes + market dispatch extract) @@ -186,7 +192,7 @@ Record these for the next migration agent; none are merge blockers for step 12. - **Managed post boundary:** `_classify_managed_post_outcome` centralizes dict → Rust classify in `managed_path.py`. - **Daemon split:** parallel/sequential market processing extracted to `cycle_market_dispatch.py`; `cycle_runner.run_once` delegates via `dispatch_selected_markets`. -## 2026-05-27 (Rust cycle kernel step 7 — typed outcomes + dispatch shrink) +## 2026-05-27 (Rust cycle engine step 7 — typed outcomes + dispatch shrink) - **Typed managed outcomes:** `ManagedActionOutcome` dataclass FFI for `classify_managed_post_result` / `classify_dexie_visibility_outcome`; Dexie visibility IO stays in `managed_path.py`, pure shaping in `items.py`. - **PlannedAction parallel planning:** `plan_parallel_managed_dispatch(expanded_actions, ctx, profiles)` — `ParallelActionReservationInput` removed from Python public surface. @@ -194,7 +200,7 @@ Record these for the next migration agent; none are merge blockers for step 12. - **Daemon splits:** `cycle_market_batch.py`, `cycle_stale_sweep.py`; `parallel_pool.py` owns thread-pool + transient cooldown tail. - **Line-count status:** `strategy_dispatch/` ~1,146 lines (`parallel_path` 90, `parallel_pool` 115); `cycle_runner.py` 426 (+ `cycle_market_batch` 129, `cycle_stale_sweep` 81). Exit target ~400 still in progress. -## 2026-05-27 (Rust cycle kernel step 7 — parallel dispatch collapse) +## 2026-05-27 (Rust cycle engine step 7 — parallel dispatch collapse) - **Single Rust call after IO:** `plan_parallel_managed_dispatch(actions, ctx, profiles)` builds prep internally; Python fetches Coinset profiles for `{base, quote, fee}` from `parallel_reservation_context.py`. - **Dead API removed:** `plan_parallel_submission_batch`, `ParallelSubmissionEntry`, `build_parallel_reservation_prep`, and `prepare_parallel_managed_submission_decision` dropped from Python surface. @@ -202,7 +208,7 @@ Record these for the next migration agent; none are merge blockers for step 12. - **Daemon split:** websocket handlers extracted to `cycle_ws_handlers.py`; `parallel_plan.py` deleted (worker uses `ParallelQueueItem` directly). - **Managed post glue:** `managed_action_item_from_post` centralizes Rust classify + Dexie visibility in `items.py`. -## 2026-05-27 (Rust cycle kernel step 7 — strategy_dispatch quality follow-up) +## 2026-05-27 (Rust cycle engine step 7 — strategy_dispatch quality follow-up) - **Typed parallel reservation FFI:** `ParallelReservationContext` and `ParallelActionReservationInput` dataclasses — no JSON dict shuttle. - **Single planning call after IO:** superseded by collapsed `plan_parallel_managed_dispatch(actions, ctx, profiles)` above. @@ -211,32 +217,32 @@ Record these for the next migration agent; none are merge blockers for step 12. - **strategy_dispatch split:** `parallel_plan.py`, `parallel_worker.py`; deleted dead `reservation_request_for_action`. - **Honest migration status:** `strategy_dispatch/` package is still ~1,155 lines (exit target ~400 not met); structural quality improved but line-count shrink is incremental. -## 2026-05-27 (Rust cycle kernel step 7 — strategy_dispatch reservation + retry) +## 2026-05-27 (Rust cycle engine step 7 — strategy_dispatch reservation + retry) - **Parallel reservation prep in Rust:** reservation request shaping moved into `build_parallel_reservation_prep` (typed prep for asset-id discovery before Coinset IO). - **Managed retry decision in Rust:** `managed_retry_decision` consolidates retry/stop policy for the managed post loop. - **Note:** initial step-7 landing used dict-shuttle FFI; superseded by typed follow-up above. -## 2026-05-27 (Rust cycle kernel step 6 — cycle runner extraction) +## 2026-05-27 (Rust cycle engine step 6 — cycle runner extraction) - **New module:** `greenfloor/daemon/cycle_runner.py` — `run_once`, `run_loop`, market batch selection, stale-open sweep, disabled-market throttling, and reload-marker consumption (~680 lines of IO glue). - **`main.py` slimmed to ~176 lines:** CLI entrypoint (`argparse` + `main()`), daemon instance lock, and lock-conflict handling only. - **Testing surface:** `greenfloor/daemon/testing/main` aliases `cycle_runner` for adapter monkeypatches; CLI/lock tests import `greenfloor.daemon.main` directly. - **Migration status:** step 6 complete for `main.py` (under ~400-line exit target). **`strategy_dispatch` package** remains the next shrink target. -## 2026-05-27 (Rust cycle kernel step 5 — execution + typed FFI) +## 2026-05-27 (Rust cycle engine step 5 — execution + typed FFI) -- **Rust execution plan:** `greenfloor-signer/src/cycle/execution.rs` — `plan_parallel_submission_batch`, `expand_planned_actions`, `filter_planned_actions_with_positive_repeat`, `sequential_action_route`; parallel gating via `can_parallelize_managed_offers` only (removed `select_strategy_execution_dispatch`). +- **Rust execution plan:** `greenfloor-engine/src/cycle/execution.rs` — `plan_parallel_submission_batch`, `expand_planned_actions`, `filter_planned_actions_with_positive_repeat`, `sequential_action_route`; parallel gating via `can_parallelize_managed_offers` only (removed `select_strategy_execution_dispatch`). - **Typed strategy FFI:** PyO3 returns `PlannedAction` / `ParallelSubmissionEntry` / `ParallelBatchPlan` dataclasses; bridge uses `planned_actions_from_signer_list` (strict type checks, no per-item dict shuttle). `greenfloor/core/planned_action.py` and `greenfloor/core/strategy_types.py` break the cycle↔strategy import loop. -- **Orchestration FFI:** `greenfloor/core/cycle_orchestration.py` + `greenfloor-signer-pyo3/src/cycle/orchestration_py.rs` — `MarketBatchSelection`, `OfferStateRow`, `StaleSweepCandidate`, `StaleSweepHit`, `StaleSweepProgress`, `ParallelActionOutcome`; `main.py` stale sweep and market batch use attributes (not dict `.get()`). -- **PyO3 layout:** monolithic `cycle.rs` split into `greenfloor-signer-pyo3/src/cycle/` (`managed_py`, `market_py`, `stale_sweep_py`, `offer_py`, …); `execution_py.rs` for batch planning bindings. +- **Orchestration FFI:** `greenfloor/core/cycle_orchestration.py` + `greenfloor-engine-pyo3/src/cycle/orchestration_py.rs` — `MarketBatchSelection`, `OfferStateRow`, `StaleSweepCandidate`, `StaleSweepHit`, `StaleSweepProgress`, `ParallelActionOutcome`; `main.py` stale sweep and market batch use attributes (not dict `.get()`). +- **PyO3 layout:** monolithic `cycle.rs` split into `greenfloor-engine-pyo3/src/cycle/` (`managed_py`, `market_py`, `stale_sweep_py`, `offer_py`, …); `execution_py.rs` for batch planning bindings. - **Python packages:** `strategy_dispatch` and `market_cycle` are multi-module packages; `StrategyDispatchHooks` + explicit callables (no `dispatch_pkg` self-imports, no `strategy_dispatch._*` test aliases); `parallel_batch.build_parallel_dispatch_plan`; strategy phase split `strategy_eval_phase` / `strategy_exec_phase` (`StrategyConfig`, `datetime` on `MarketCycleRun.now`); `reservation_helpers` takes `MarketConfig`. - **Core cycle surface:** `core/cycle` package (`_bridge_managed`, `_bridge_orchestration`, `policy`, `__init__`); public expand name `expand_planned_actions` only. - **Migration status:** step 5 complete for strategy dispatch + orchestration typing; step 6 extracted cycle runner from `main.py`. -## 2026-05-27 (Rust cycle kernel — quality review follow-up) +## 2026-05-27 (Rust cycle engine — quality review follow-up) -- Split cycle PyO3 bindings into `greenfloor-signer-pyo3/src/cycle.rs` + shared `py_utils.rs`; `lib.rs` back under 500 lines. +- Split cycle PyO3 bindings into `greenfloor-engine-pyo3/src/cycle.rs` + shared `py_utils.rs`; `lib.rs` back under 500 lines. - Collapsed four pass-through `core/cycle_*.py` modules into single `greenfloor/core/cycle.py`. - Split `core/cycle` into package: `_bridge.py` (PyO3 only) + `__init__.py` (re-exports and API-shaping wrappers only). - Typed PyO3 dict extraction for size-count maps (`dict_to_i64_i64_map`); no JSON round-trip on aggregate/one-sided paths. @@ -247,9 +253,9 @@ Record these for the next migration agent; none are merge blockers for step 12. - Single source of truth for phase order: Rust `market_cycle_phases()` → Python `MARKET_CYCLE_PHASES`. - **Migration status:** steps 1–4 complete; **exit criteria not met** (`main.py` ~850, `strategy_dispatch.py` ~840 vs ~400 target). Step 5 (execution plan in Rust) required before calling migration complete. -## 2026-05-27 (Rust cycle kernel step 4 — per-market phase runner) +## 2026-05-27 (Rust cycle engine step 4 — per-market phase runner) -- Added `greenfloor-signer/src/cycle/market.rs`: cycle phase enum/order, `MarketCycleResultState` merge helpers, inventory fallback gating, inventory scan source resolution, tracked-size resolution, and two-sided offer count aggregation. +- Added `greenfloor-engine/src/cycle/market.rs`: cycle phase enum/order, `MarketCycleResultState` merge helpers, inventory fallback gating, inventory scan source resolution, tracked-size resolution, and two-sided offer count aggregation. - Exposed market-phase helpers via PyO3; Python `greenfloor/core/cycle.py` is the policy surface. - Extracted `greenfloor/daemon/market_cycle.py` from `main.py`; `process_single_market` now walks Rust `MARKET_CYCLE_PHASES` via a phase runner table. - **Step 4 scope note:** this extraction was a **relocation** of existing per-market logic into a dedicated module — phase bodies and IO flow are unchanged. A **phase-table-driven restructure** (Rust-owned phase dispatch with thin Python IO hooks per phase) is deferred to the secondary follow-up below. @@ -263,7 +269,7 @@ Record these for the next migration agent; none are merge blockers for step 12. | `greenfloor/daemon/strategy_dispatch.py` | ~840 | ~400 | | `greenfloor/daemon/market_cycle.py` | ~683 | (IO glue; no target) | -**Best next step (step 5):** extract the **strategy action execution plan** from `strategy_dispatch.py` into Rust (`greenfloor-signer/src/cycle/execution.rs`). Move pure branching that remains in `_execute_actions_parallel`, `_execute_actions_sequential`, and `_prepare_parallel_managed_submission` — reservation grouping, parallel vs sequential gating, retry scheduling inputs, and per-action outcome shaping — into deterministic structs. Python keeps reservation SQLite IO, Dexie visibility polling, offer build/post, and thread-pool dispatch only. This is the highest-leverage hunk: `strategy_dispatch.py` is still ~840 lines despite step 2 moving managed _decisions_ to Rust; most of what remains is orchestration loops that can become thin interpreters over Rust outputs. +**Best next step (step 5):** extract the **strategy action execution plan** from `strategy_dispatch.py` into Rust (`greenfloor-engine/src/cycle/execution.rs`). Move pure branching that remains in `_execute_actions_parallel`, `_execute_actions_sequential`, and `_prepare_parallel_managed_submission` — reservation grouping, parallel vs sequential gating, retry scheduling inputs, and per-action outcome shaping — into deterministic structs. Python keeps reservation SQLite IO, Dexie visibility polling, offer build/post, and thread-pool dispatch only. This is the highest-leverage hunk: `strategy_dispatch.py` is still ~840 lines despite step 2 moving managed _decisions_ to Rust; most of what remains is orchestration loops that can become thin interpreters over Rust outputs. Secondary follow-ups after step 5: @@ -271,43 +277,43 @@ Secondary follow-ups after step 5: - **`market_cycle.py` strategy phase** — move two-sided action planning (`strategy_state.py`) into Rust `cycle/strategy.rs` extensions; sell-only gap reseed is already in `cycle/reseed.rs` (step 8). - **`greenfloor/storage/sqlite.py`** (~690 lines) — defer until daemon glue hits exit criteria; schema/query helpers are not on the critical offer-post path. -## 2026-05-27 (Rust cycle kernel step 3 — main market cycle orchestration) +## 2026-05-27 (Rust cycle engine step 3 — main market cycle orchestration) -- Added `greenfloor-signer/src/cycle/orchestration.rs`: market batch selection (immediate requeue + round-robin), immediate-requeue deque shaping, stale-open sweep candidate filtering and Dexie hit classification, disabled-market log throttling, slot-dispatch gating, and CAT inventory fallback gating. +- Added `greenfloor-engine/src/cycle/orchestration.rs`: market batch selection (immediate requeue + round-robin), immediate-requeue deque shaping, stale-open sweep candidate filtering and Dexie hit classification, disabled-market log throttling, slot-dispatch gating, and CAT inventory fallback gating. - Exposed orchestration helpers via PyO3; Python `greenfloor/core/cycle.py` is the policy surface. - `greenfloor/daemon/main.py` delegates batch selection, stale sweep decisions, disabled-market throttling, and slot-dispatch gates to Rust; Python retains SQLite/Dexie IO, per-market cycle execution, and CLI entrypoints. - Canonical cycle phase order documented as `reconcile → inventory → strategy → cancel → coin_ops`. -## 2026-05-27 (Rust cycle kernel step 2 — managed dispatch path) +## 2026-05-27 (Rust cycle engine step 2 — managed dispatch path) -- Added `greenfloor-signer/src/cycle/managed.rs`: transient-error classification, managed-post outcome decisions, Dexie visibility gating, parallel submission gating, reservation release status, retry backoff/sleep policy, and parallel transient cooldown threshold. +- Added `greenfloor-engine/src/cycle/managed.rs`: transient-error classification, managed-post outcome decisions, Dexie visibility gating, parallel submission gating, reservation release status, retry backoff/sleep policy, and parallel transient cooldown threshold. - Exposed managed-path helpers via PyO3; Python `greenfloor/core/cycle.py` is the deterministic policy surface. - `greenfloor/daemon/cooldowns.py` and `greenfloor/daemon/strategy_dispatch.py` delegate managed-path decisions to Rust; Python retains reservation SQLite IO, Dexie visibility polling, and offer post execution. - `greenfloor/runtime/offer_publish.py` re-exports Dexie 404 transient detection from the Rust-backed core helper. -## 2026-05-27 (Rust cycle kernel — strategy, lifecycle, dispatch) +## 2026-05-27 (Rust cycle engine — strategy, lifecycle, dispatch) -- Added `greenfloor-signer/src/cycle/` as the first daemon migration hunk per the extraction plan in this log: pure decision logic for strategy evaluation, offer lifecycle transitions, action expansion, reservation request shaping, and single-input combine gating. -- Exposed cycle kernel via PyO3 (`evaluate_market`, `apply_offer_signal`, `expiry_seconds_for_action`, `reservation_request_for_managed_offer`, `single_input_preferred_skip_reason`). +- Added `greenfloor-engine/src/cycle/` as the first daemon migration hunk per the extraction plan in this log: pure decision logic for strategy evaluation, offer lifecycle transitions, action expansion, reservation request shaping, and single-input combine gating. +- Exposed cycle engine via PyO3 (`evaluate_market`, `apply_offer_signal`, `expiry_seconds_for_action`, `reservation_request_for_managed_offer`, `single_input_preferred_skip_reason`). - Python `greenfloor/core/strategy.py`, `offer_lifecycle.py`, and `cycle.py` delegate to Rust; `strategy_dispatch.py` imports core helpers (removed ~80 lines of duplicated pure logic). ## 2026-05-27 (daemon Rust extraction plan — main / strategy_dispatch) Large Python daemon modules remain intentionally unsplit pending Rust migration (see ADR 0006 signer canonical path). Extraction order: -1. **`greenfloor-signer` cycle kernel (first)** ✅ — pure decision structs for strategy action expansion, reservation request shaping, and offer lifecycle transition inputs. Python keeps IO adapters only. +1. **`greenfloor-engine` cycle engine (first)** ✅ — pure decision structs for strategy action expansion, reservation request shaping, and offer lifecycle transition inputs. Python keeps IO adapters only. 2. **`strategy_dispatch` managed path (second)** ✅ — parallel reservation acquire/release decisions, managed offer post retry classification, and transient-error typing in Rust; Python retains Dexie visibility polling and audit persistence calls. 3. **`main` market cycle orchestration (third)** ✅ — batch selection, stale sweep classification, disabled-market throttling, slot-dispatch gating, and immediate-requeue bookkeeping in Rust; Python retains SQLite/Dexie IO. 4. **Per-market phase runner (fourth)** ✅ — inventory source selection, tracked sizes, result-state merges, and phase ordering in Rust; Python IO **relocated** to `market_cycle.py` (not yet restructured around a Rust phase table). 5. **Strategy action execution plan (fifth)** ✅ — parallel vs sequential batch planning and typed orchestration FFI in Rust; Python retains thread pools, reservation SQLite, and offer build/post only. 6. **`main.py` cycle runner extraction (sixth)** ✅ — `run_once` / `run_loop` moved to `greenfloor/daemon/cycle_runner.py`; `main.py` retains CLI entrypoint and instance lock only. -7. **`strategy_dispatch` reservation + retry kernel (seventh)** ✅ — typed `ManagedActionOutcome`, `PlannedAction` parallel planning, `parallel_pool` extract. +7. **`strategy_dispatch` reservation + retry engine (seventh)** ✅ — typed `ManagedActionOutcome`, `PlannedAction` parallel planning, `parallel_pool` extract. 8. **Market-cycle reseed gap planning (eighth)** ✅ — `cycle/reseed.rs` + typed PyO3 `ReseedGapPlan`; Python keeps SQLite offer-count IO and structured reseed logging; `tests/test_cycle_reseed.py` enforces skip-reason label parity. -9. **Offer reconciliation transition kernel (ninth)** ✅ — `cycle/reconcile.rs` + typed PyO3 `CycleOfferTransition`; Python keeps Dexie fetch, SQLite tx-signal lookup, and audit persistence in `runtime/offer_reconciliation.py`. +9. **Offer reconciliation transition engine (ninth)** ✅ — `cycle/reconcile.rs` + typed PyO3 `CycleOfferTransition`; Python keeps Dexie fetch, SQLite tx-signal lookup, and audit persistence in `runtime/offer_reconciliation.py`. 10. **Core coin-op policy bundle (tenth)** ✅ — `coin_ops/{plan,fee_budget,inventory,policy}.rs` + PyO3 `coin_ops_py.rs`; Python keeps `runtime/coin_ops/` execution IO and `daemon/coin_ops_cycle.py` glue. 11. **Coin-op selection/planning helpers (eleventh)** ✅ — `coin_ops/selection.rs` + `split_planning.rs`; Python keeps CLI/daemon execution IO only (`runtime/coin_ops/planning.py` / `selection.py` re-exports). -12. **Shared hex utilities + low-inventory alerts (twelfth)** ✅ — `hex.rs` + `cycle/notifications.rs` + PyO3 bindings; pure-Python `hex_utils.py`; typed `DeterministicPolicyKernelProtocol`. -13. **Cancel-policy decision kernel (thirteenth)** ✅ — `cycle/cancel.rs` + typed `OpenOfferRow`; Python keeps Dexie cancel IO and SQLite persistence. +12. **Shared hex utilities + low-inventory alerts (twelfth)** ✅ — `hex.rs` + `cycle/notifications.rs` + PyO3 bindings; pure-Python `hex_utils.py`; typed `DeterministicPolicyEngineProtocol`. +13. **Cancel-policy decision engine (thirteenth)** ✅ — `cycle/cancel.rs` + typed `OpenOfferRow`; Python keeps Dexie cancel IO and SQLite persistence. **Exit criteria:** `greenfloor/daemon/main.py` and `greenfloor/daemon/strategy_dispatch/` each under ~400 lines of Python glue; Rust crates absorb complexity; Python keeps SQLite, Dexie, websocket, and CLI. **`strategy_dispatch/` ✅ (386 lines).** `main.py` satisfied via `cycle_runner.py` extraction (step 6). @@ -336,10 +342,10 @@ Large Python daemon modules remain intentionally unsplit pending Rust migration ## 2026-05-26 (Rust signer PyO3 migration — vault offers via coinset MSP) - Added ADR 0007: vault KMS signing, offers, bootstrap, and asset resolution run through - in-process `greenfloor_signer` (PyO3) backed by `greenfloor-signer`; chain IO via + in-process `greenfloor_engine` (PyO3) backed by `greenfloor-engine`; chain IO via `api-msp.coinset.org`; vault metadata from `program.yaml` `signer:` + `vault:` blocks. -- Replaced `greenfloor-signer/src/cloud_wallet.rs` GraphQL client with `coinset/msp.rs`. -- Added `greenfloor-signer-pyo3` crate (`resolve_vault_context`, `build_vault_cat_offer`, +- Replaced `greenfloor-engine/src/cloud_wallet.rs` GraphQL client with `coinset/msp.rs`. +- Added `greenfloor-engine-pyo3` crate (`resolve_vault_context`, `build_vault_cat_offer`, `build_mixed_split`, `resolve_offer_asset_ids`); CI builds wheel before pytest. - Python vault paths in `signing.py` delegate to `greenfloor/adapters/rust_signer.py`; signer offer build/post lives in `greenfloor/runtime/offer_runtime.py` (Cloud Wallet phases in `greenfloor/runtime/cloud_wallet/`). @@ -350,9 +356,9 @@ Large Python daemon modules remain intentionally unsplit pending Rust migration `offer_publish.py`, `OfferPostRequest` dispatch, canonical `offer_execution_backend()` / `managed_offer_execution_backend()` routing gates (ADR 0008). -## 2026-05-26 (greenfloor-signer quality fixes + Rust canonical path ADR) +## 2026-05-26 (greenfloor-engine quality fixes + Rust canonical path ADR) -- Added ADR 0006: `greenfloor-signer` is the canonical vault KMS signing implementation; Python `signing.py` is legacy during migration. +- Added ADR 0006: `greenfloor-engine` is the canonical vault KMS signing implementation; Python `signing.py` is legacy during migration. - Fixed presplit offer nonce to derive once from source offer coin ids (ent-wallet parity); removed placeholder nonce bug. - Refactored offer orchestration (`OfferPlan`, `PresplitOfferBinding`, `VaultSession`, shared CAT parsing, `KmsSigner`, mode-23 message module). - Collapsed CLI mixed-split commands into `mixed-cat` with aliases (`split-cat`, `send-cat`, `combine-cat`). @@ -360,7 +366,7 @@ Large Python daemon modules remain intentionally unsplit pending Rust migration - Shared vault hash golden vectors in `tests/fixtures/vault_hash_golden.json`. - Simulator presplit+offer test now validates puzzle-hash binding and executes atomic take on simulator. -## 2026-05-21 (greenfloor-signer Phase 4: pre-split vault CAT offers) +## 2026-05-21 (greenfloor-engine Phase 4: pre-split vault CAT offers) - Added `offer/presplit.rs` implementing ent-wallet-style pre-split: vault-signed split tx mints `P2_CONDITIONS_OR_SINGLETON` presplit CAT coins; final offer bundle spends via CONDITIONS MIPS path (no vault singleton, fast-forward safe). - Extended `create-offer` CLI: `--split-input-coins`, `--broadcast-split`, `--presplit-coin-ids`, `--expires-at`. @@ -368,24 +374,24 @@ Large Python daemon modules remain intentionally unsplit pending Rust migration - `--presplit-coin-ids` builds offer from an existing presplit coin (post-split step). Single source cat required for split tx. - Unit tests for presplit gating, puzzle hash derivation, and fixed settlement conditions. -## 2026-05-21 (greenfloor-signer Phase 3: vault CAT offer builder) +## 2026-05-21 (greenfloor-engine Phase 3: vault CAT offer builder) - Added `offer/build.rs` with `build_vault_cat_offer()` using `Offer::from_input_spend_bundle` + `encode_offer` (offer-compression feature on `chia-sdk-driver`). - Refactored shared vault CAT spend materialization into `materialize_vault_cat_finished_spends()` (used by mixed-split and offers). - New CLI command: `create-offer` for vault-signed CAT→XCH/CAT offers (no pre-split; inputs must cover offer amount). Vault local path supports CAT offer side only, matching Python `signing.py`. -- `cargo build` and `cargo test` pass for `greenfloor-signer`. +- `cargo build` and `cargo test` pass for `greenfloor-engine`. -## 2026-05-21 (greenfloor-signer Phase 2: vault CAT spend orchestration) +## 2026-05-21 (greenfloor-engine Phase 2: vault CAT spend orchestration) -- Added coinset-backed CAT discovery, vault singleton resolution, and KMS-signed vault spend append to `greenfloor-signer`. +- Added coinset-backed CAT discovery, vault singleton resolution, and KMS-signed vault spend append to `greenfloor-engine`. - New CLI commands: `split-cat`, `send-cat`, `combine-cat` (with optional `--broadcast`) for vault-owned CAT mixed-split spends without fees. - Mirrors Python `signing.py` vault path: MIPS inner spends, mode-23 message relay, SECP256R1 fast-forward KMS signing, infinity BLS aggregate. -## 2026-05-21 (greenfloor-signer Phase 1: vault-info foundation) +## 2026-05-21 (greenfloor-engine Phase 1: vault-info foundation) -- Added `greenfloor-signer/` Rust CLI crate backed by `chia-wallet-sdk` path deps. +- Added `greenfloor-engine/` Rust CLI crate backed by `chia-wallet-sdk` path deps. - Phase 1 delivers `vault-info`: loads `program.yaml` `cloud_wallet` block, fetches custody snapshot via GraphQL (chia-user-key PEM auth), derives vault puzzle hashes (inner/custody/recovery/P2 singleton), resolves KMS P-256 public key, and validates KMS pubkey matches the sole SECP256R1 custody key. -- CI builds and tests `greenfloor-signer` alongside `greenfloor-native`. +- CI builds and tests `greenfloor-engine` alongside `greenfloor-native`. ## 2026-03-30 (audit follow-up: daemon/CLI boundary + offer log level + retry tests + subprocess override policy) diff --git a/greenfloor-engine-pyo3/Cargo.lock b/greenfloor-engine-pyo3/Cargo.lock index b585f405..bc1b848d 100644 --- a/greenfloor-engine-pyo3/Cargo.lock +++ b/greenfloor-engine-pyo3/Cargo.lock @@ -1666,7 +1666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] -name = "greenfloor-signer" +name = "greenfloor-engine" version = "0.1.0" dependencies = [ "anyhow", @@ -1703,11 +1703,11 @@ dependencies = [ ] [[package]] -name = "greenfloor-signer-pyo3" +name = "greenfloor-engine-pyo3" version = "0.1.0" dependencies = [ "chia-bls 0.36.1", - "greenfloor-signer", + "greenfloor-engine", "pyo3", "serde_json", "tokio", diff --git a/greenfloor-engine-pyo3/Cargo.toml b/greenfloor-engine-pyo3/Cargo.toml index e6a1bd0a..991bb817 100644 --- a/greenfloor-engine-pyo3/Cargo.toml +++ b/greenfloor-engine-pyo3/Cargo.toml @@ -1,15 +1,15 @@ [package] -name = "greenfloor-signer-pyo3" +name = "greenfloor-engine-pyo3" version = "0.1.0" edition = "2021" [lib] -name = "greenfloor_kernel" +name = "greenfloor_engine" crate-type = ["cdylib"] [dependencies] chia-bls = "0.36.1" -greenfloor-signer = { path = "../greenfloor-signer" } +engine-core = { package = "greenfloor-engine", path = "../greenfloor-engine" } pyo3 = { version = "0.28.2", features = ["extension-module"] } serde_json = "1" tokio = { version = "1", features = ["rt-multi-thread"] } diff --git a/greenfloor-engine-pyo3/pyproject.toml b/greenfloor-engine-pyo3/pyproject.toml index 0c7a8933..5c4575cc 100644 --- a/greenfloor-engine-pyo3/pyproject.toml +++ b/greenfloor-engine-pyo3/pyproject.toml @@ -3,9 +3,9 @@ requires = ["maturin>=1.8,<2.0"] build-backend = "maturin" [project] -name = "greenfloor_kernel" +name = "greenfloor_engine" version = "0.1.0" requires-python = ">=3.11" [tool.maturin] -module-name = "greenfloor_kernel" +module-name = "greenfloor_engine" diff --git a/greenfloor-engine-pyo3/src/coin_ops_py.rs b/greenfloor-engine-pyo3/src/coin_ops_py.rs index 0c12f8d0..ce079646 100644 --- a/greenfloor-engine-pyo3/src/coin_ops_py.rs +++ b/greenfloor-engine-pyo3/src/coin_ops_py.rs @@ -2,7 +2,7 @@ use pyo3::exceptions::PyValueError; use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict, PyList}; -use signer_core::{ +use engine_core::{ amount_meets_coin_op_min_mojos, coin_op_min_amount_mojos, coin_op_should_stop, coin_op_target_amount_allowed, compute_bucket_counts_from_coins, effective_sell_bucket_counts_for_coin_ops, evaluate_coin_combine_gate, diff --git a/greenfloor-engine-pyo3/src/cycle/cancel_py.rs b/greenfloor-engine-pyo3/src/cycle/cancel_py.rs index 7b37f53f..54fb1de4 100644 --- a/greenfloor-engine-pyo3/src/cycle/cancel_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/cancel_py.rs @@ -1,6 +1,6 @@ use pyo3::prelude::*; use pyo3::types::PyList; -use signer_core::{ +use engine_core::{ abs_move_bps, cancel_move_threshold_bps, collect_open_offer_ids_for_cancel, evaluate_cancel_policy_decision, }; diff --git a/greenfloor-engine-pyo3/src/cycle/inventory_py.rs b/greenfloor-engine-pyo3/src/cycle/inventory_py.rs index cf0d48a4..3034f8be 100644 --- a/greenfloor-engine-pyo3/src/cycle/inventory_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/inventory_py.rs @@ -1,6 +1,6 @@ use pyo3::prelude::*; -use signer_core::{needs_inventory_fallback, resolve_inventory_scan_source}; +use engine_core::{needs_inventory_fallback, resolve_inventory_scan_source}; #[pyfunction] #[pyo3(name = "needs_inventory_fallback")] diff --git a/greenfloor-engine-pyo3/src/cycle/managed_py.rs b/greenfloor-engine-pyo3/src/cycle/managed_py.rs index 67a3b403..0c321b89 100644 --- a/greenfloor-engine-pyo3/src/cycle/managed_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/managed_py.rs @@ -6,7 +6,7 @@ use crate::py_utils::{ managed_action_outcome_to_py, managed_retry_decision_class, request_dict_to_json, to_py_err, }; -use signer_core::{ +use engine_core::{ can_parallelize_managed_offers, classify_dexie_visibility_outcome, classify_managed_post_result, classify_managed_transient_error, count_parallel_transient_failures, is_managed_upstream_transient_error, @@ -156,7 +156,7 @@ fn managed_retry_decision_py( "should_retry", matches!( decision.decision, - signer_core::ManagedRetryDecisionKind::Retry + engine_core::ManagedRetryDecisionKind::Retry ), )?; kwargs.set_item("sleep_ms", decision.sleep_ms)?; diff --git a/greenfloor-engine-pyo3/src/cycle/market_py.rs b/greenfloor-engine-pyo3/src/cycle/market_py.rs index 2dc06a0e..444525ff 100644 --- a/greenfloor-engine-pyo3/src/cycle/market_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/market_py.rs @@ -1,6 +1,6 @@ use pyo3::prelude::*; -use signer_core::{ +use engine_core::{ dedupe_sorted_market_ids, enqueue_immediate_requeue, market_cycle_phases, next_disabled_market_log_deadline, select_market_batch, should_log_disabled_market, should_try_cat_inventory_fallback, should_use_market_slot_dispatch, diff --git a/greenfloor-engine-pyo3/src/cycle/offer_py.rs b/greenfloor-engine-pyo3/src/cycle/offer_py.rs index 2417c790..5b293320 100644 --- a/greenfloor-engine-pyo3/src/cycle/offer_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/offer_py.rs @@ -3,7 +3,7 @@ use pyo3::prelude::*; use crate::py_utils::{dict_from_json_value, to_py_err}; -use signer_core::{ +use engine_core::{ apply_offer_signal, expiry_seconds_for_action, OfferLifecycleState, OfferSignal, }; diff --git a/greenfloor-engine-pyo3/src/cycle/orchestration_py.rs b/greenfloor-engine-pyo3/src/cycle/orchestration_py.rs index 08e31c80..2a7fa929 100644 --- a/greenfloor-engine-pyo3/src/cycle/orchestration_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/orchestration_py.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; use pyo3::types::{PyDict, PyList}; -use signer_core::{ +use engine_core::{ ManagedActionStatus, MarketBatchSelection, OfferStateRow, StaleSweepCandidate, StaleSweepHit, StaleSweepProgress, }; diff --git a/greenfloor-engine-pyo3/src/cycle/reconcile_py.rs b/greenfloor-engine-pyo3/src/cycle/reconcile_py.rs index 24893db8..b237bc70 100644 --- a/greenfloor-engine-pyo3/src/cycle/reconcile_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/reconcile_py.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; use pyo3::types::PyDict; -use signer_core::{ +use engine_core::{ resolve_missing_watched_offer_transition, resolve_watched_offer_transition_from_signals, unchanged_offer_transition, unsupported_venue_offer_transition, CycleOfferTransition, }; diff --git a/greenfloor-engine-pyo3/src/cycle/stale_sweep_py.rs b/greenfloor-engine-pyo3/src/cycle/stale_sweep_py.rs index ef5c1685..c2644892 100644 --- a/greenfloor-engine-pyo3/src/cycle/stale_sweep_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/stale_sweep_py.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; use pyo3::types::PyList; -use signer_core::{ +use engine_core::{ classify_dexie_stale_offer_status, collect_stale_sweep_candidates, is_dexie_offer_missing_error_text, record_stale_sweep_check, }; diff --git a/greenfloor-engine-pyo3/src/cycle/strategy_counts_py.rs b/greenfloor-engine-pyo3/src/cycle/strategy_counts_py.rs index 07d471af..a9c951d2 100644 --- a/greenfloor-engine-pyo3/src/cycle/strategy_counts_py.rs +++ b/greenfloor-engine-pyo3/src/cycle/strategy_counts_py.rs @@ -5,7 +5,7 @@ use crate::py_utils::{ dict_to_i64_i64_map, i64_i64_map_to_py_dict, strategy_action_sell_counts_from_py_list, }; -use signer_core::{ +use engine_core::{ aggregate_two_sided_offer_counts, executed_sell_offer_counts_by_size, is_two_sided_market_mode, one_sided_offer_counts_by_side, resolve_tracked_sizes, }; diff --git a/greenfloor-engine-pyo3/src/execution_py.rs b/greenfloor-engine-pyo3/src/execution_py.rs index 04b537bb..187fd93b 100644 --- a/greenfloor-engine-pyo3/src/execution_py.rs +++ b/greenfloor-engine-pyo3/src/execution_py.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; use pyo3::types::{PyDict, PyList}; -use signer_core::{ +use engine_core::{ expand_planned_actions, filter_planned_actions_with_positive_repeat, plan_parallel_managed_dispatch, sequential_action_route, ParallelBatchPlan, ParallelReservationContext, SequentialActionRoute, diff --git a/greenfloor-engine-pyo3/src/hex_py.rs b/greenfloor-engine-pyo3/src/hex_py.rs index 242cd9d6..ecdbed1f 100644 --- a/greenfloor-engine-pyo3/src/hex_py.rs +++ b/greenfloor-engine-pyo3/src/hex_py.rs @@ -1,5 +1,5 @@ use pyo3::prelude::*; -use signer_core::{ +use engine_core::{ default_mojo_multiplier_for_asset, is_canonical_xch_asset, is_hex_id, normalize_hex_id, }; diff --git a/greenfloor-engine-pyo3/src/lib.rs b/greenfloor-engine-pyo3/src/lib.rs index bcd3e4f2..10302f3c 100644 --- a/greenfloor-engine-pyo3/src/lib.rs +++ b/greenfloor-engine-pyo3/src/lib.rs @@ -1,8 +1,7 @@ -//! PyO3 bindings for the GreenFloor Rust kernel (`greenfloor-signer` crate). +//! PyO3 bindings for the GreenFloor Rust engine (`greenfloor-engine` crate). //! -//! The extension module is exported as `greenfloor_kernel` (ADR 0010). Python -//! callers should import through `greenfloor.core.kernel_bridge.import_kernel`. -extern crate greenfloor_signer as signer_core; +//! The extension module is exported as `greenfloor_engine` (ADR 0010). Python +//! callers should import through `greenfloor.core.engine_bridge.import_engine`. mod coin_ops_py; mod cycle; @@ -24,8 +23,8 @@ use std::sync::OnceLock; use chia_bls::SecretKey; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; -use signer_core::error::{bls_reason, broadcast_reason, BlsOp}; -use signer_core::{ +use engine_core::error::{bls_reason, broadcast_reason, BlsOp}; +use engine_core::{ broadcast_bls_spend_bundle, build_and_optionally_broadcast_vault_cat_mixed_split, build_bls_mixed_split_spend_bundle, build_bls_offer_spend_bundle, build_bls_xch_coin_op_spend_bundle, build_vault_cat_offer, @@ -57,9 +56,9 @@ fn parse_master_sk_bytes(master_sk_bytes: &[u8]) -> PyResult { SecretKey::from_bytes(&bytes).map_err(to_py_err) } -fn block_on_signer(future: F) -> Result +fn block_on_engine(future: F) -> Result where - F: Future>, + F: Future>, { runtime().block_on(future) } @@ -67,7 +66,7 @@ where fn bls_build_dict_py( py: Python<'_>, op: BlsOp, - result: Result, + result: Result, fill_ok: impl FnOnce(&Bound<'_, PyDict>, T) -> PyResult<()>, ) -> PyResult> { let dict = PyDict::new(py); @@ -136,7 +135,7 @@ fn build_bls_mixed_split_py( let payload = request_dict_to_json(request)?; let split_request: BlsMixedSplitRequest = serde_json::from_value(payload).map_err(to_py_err)?; - let result = block_on_signer(build_bls_mixed_split_spend_bundle( + let result = block_on_engine(build_bls_mixed_split_spend_bundle( network, &master_sk, split_request, @@ -162,7 +161,7 @@ fn build_bls_offer_py( let master_sk = parse_master_sk_bytes(master_sk_bytes)?; let payload = request_dict_to_json(request)?; let offer_request: BlsOfferRequest = serde_json::from_value(payload).map_err(to_py_err)?; - let result = block_on_signer(build_bls_offer_spend_bundle( + let result = block_on_engine(build_bls_offer_spend_bundle( network, &master_sk, offer_request, @@ -186,7 +185,7 @@ fn build_bls_xch_coin_op_py( let payload = request_dict_to_json(request)?; let coin_op_request: BlsXchCoinOpRequest = serde_json::from_value(payload).map_err(to_py_err)?; - let result = block_on_signer(build_bls_xch_coin_op_spend_bundle( + let result = block_on_engine(build_bls_xch_coin_op_spend_bundle( network, &master_sk, coin_op_request, @@ -226,7 +225,7 @@ fn list_bls_cat_coins_by_ids_py(network: &str, coin_ids: Vec) -> PyResul fn summaries_to_py_list( py: Python<'_>, - summaries: Vec, + summaries: Vec, ) -> PyResult> { let list = PyList::empty(py); for summary in summaries { @@ -412,7 +411,7 @@ fn coinset_get_conservative_fee_estimate_py( } #[pymodule] -fn greenfloor_kernel(m: &Bound<'_, PyModule>) -> PyResult<()> { +fn greenfloor_engine(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(resolve_vault_context_py, m)?)?; m.add_function(wrap_pyfunction!(build_vault_cat_offer_py, m)?)?; m.add_function(wrap_pyfunction!(build_mixed_split_py, m)?)?; diff --git a/greenfloor-engine-pyo3/src/notifications_py.rs b/greenfloor-engine-pyo3/src/notifications_py.rs index 606fd2a3..3ad36ecd 100644 --- a/greenfloor-engine-pyo3/src/notifications_py.rs +++ b/greenfloor-engine-pyo3/src/notifications_py.rs @@ -1,5 +1,5 @@ use pyo3::prelude::*; -use signer_core::evaluate_low_inventory_alert; +use engine_core::evaluate_low_inventory_alert; use crate::py_utils::{low_inventory_evaluation_to_py, low_inventory_input_from_py}; diff --git a/greenfloor-engine-pyo3/src/offer_action_py.rs b/greenfloor-engine-pyo3/src/offer_action_py.rs index 439b916a..c267d0c3 100644 --- a/greenfloor-engine-pyo3/src/offer_action_py.rs +++ b/greenfloor-engine-pyo3/src/offer_action_py.rs @@ -1,12 +1,12 @@ use pyo3::prelude::*; use pyo3::types::{PyDict, PyModule}; -use signer_core::offer::action::{ +use engine_core::offer::action::{ build_bls_offer_for_action, build_signer_offer_for_action, BuildOfferForActionRequest, }; -use signer_core::{load_bls_master_secret_key, load_signer_config}; +use engine_core::{load_bls_master_secret_key, load_signer_config}; use crate::py_utils::{dict_from_json_value, request_dict_to_json, to_py_err}; -use crate::{block_on_signer, parse_master_sk_bytes, runtime}; +use crate::{block_on_engine, parse_master_sk_bytes, runtime}; #[pyfunction] #[pyo3(name = "build_signer_offer_for_action")] @@ -35,7 +35,7 @@ fn build_bls_offer_for_action_key_py( let payload = request_dict_to_json(request)?; let offer_request: BuildOfferForActionRequest = serde_json::from_value(payload).map_err(to_py_err)?; - let result = block_on_signer(build_bls_offer_for_action( + let result = block_on_engine(build_bls_offer_for_action( network, &master_sk, offer_request, @@ -56,7 +56,7 @@ fn build_bls_offer_for_action_sk_py( let payload = request_dict_to_json(request)?; let offer_request: BuildOfferForActionRequest = serde_json::from_value(payload).map_err(to_py_err)?; - let result = block_on_signer(build_bls_offer_for_action( + let result = block_on_engine(build_bls_offer_for_action( network, &master_sk, offer_request, diff --git a/greenfloor-engine-pyo3/src/offer_build_py.rs b/greenfloor-engine-pyo3/src/offer_build_py.rs index 3a069651..d8c3c36d 100644 --- a/greenfloor-engine-pyo3/src/offer_build_py.rs +++ b/greenfloor-engine-pyo3/src/offer_build_py.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; use pyo3::types::{PyAny, PyDict}; -use signer_core::{ +use engine_core::{ bootstrap_block_error, dexie_offer_asset_expectation_error, expected_publish_asset_fields, mojo_multiplier_for_leg, resolve_offer_expiry_for_pricing, resolve_quote_price_for_pricing, }; diff --git a/greenfloor-engine-pyo3/src/offer_request_py.rs b/greenfloor-engine-pyo3/src/offer_request_py.rs index 257362f6..e364e690 100644 --- a/greenfloor-engine-pyo3/src/offer_request_py.rs +++ b/greenfloor-engine-pyo3/src/offer_request_py.rs @@ -1,6 +1,6 @@ use pyo3::prelude::*; -use signer_core::{ +use engine_core::{ compute_signer_offer_leg_amounts, normalize_offer_asset_id, normalize_offer_side, quote_mojos_for_base_size, signer_split_asset_id, }; diff --git a/greenfloor-engine-pyo3/src/py_utils/bootstrap.rs b/greenfloor-engine-pyo3/src/py_utils/bootstrap.rs index b920dff9..58423025 100644 --- a/greenfloor-engine-pyo3/src/py_utils/bootstrap.rs +++ b/greenfloor-engine-pyo3/src/py_utils/bootstrap.rs @@ -1,4 +1,4 @@ -//! PyO3 entry points for bootstrap planner and phase kernel symbols. +//! PyO3 entry points for bootstrap planner and phase engine symbols. pub(crate) use super::bootstrap_marshal::{ bootstrap_early_phase_from_py, bootstrap_executed_phase_from_py, diff --git a/greenfloor-engine-pyo3/src/py_utils/bootstrap_marshal.rs b/greenfloor-engine-pyo3/src/py_utils/bootstrap_marshal.rs index 0133a8bc..767dc317 100644 --- a/greenfloor-engine-pyo3/src/py_utils/bootstrap_marshal.rs +++ b/greenfloor-engine-pyo3/src/py_utils/bootstrap_marshal.rs @@ -5,7 +5,7 @@ use pyo3::prelude::*; use pyo3::types::{PyDict, PyList}; use super::common::cached_class; -use signer_core::{ +use engine_core::{ bootstrap_early_phase, bootstrap_executed_phase, plan_bootstrap_mixed_outputs, BootstrapCoin, BootstrapPlan, BootstrapPlanOutcome, BootstrapPhaseSnapshot, LadderDeficit, PlannerLadderRow, }; diff --git a/greenfloor-engine-pyo3/src/py_utils/coin_ops.rs b/greenfloor-engine-pyo3/src/py_utils/coin_ops.rs index a6a0678c..70a5b7d4 100644 --- a/greenfloor-engine-pyo3/src/py_utils/coin_ops.rs +++ b/greenfloor-engine-pyo3/src/py_utils/coin_ops.rs @@ -6,7 +6,7 @@ use pyo3::prelude::*; use pyo3::types::{PyDict, PyList}; use super::common::{cached_class, require_i64_attr}; -use signer_core::{CombineInputSelectionMode, SplitAutoSelectPlan, SplitPlanningProfile}; +use engine_core::{CombineInputSelectionMode, SplitAutoSelectPlan, SplitPlanningProfile}; const COIN_OPS_MODULE: &str = "greenfloor.core.coin_ops"; @@ -58,7 +58,7 @@ pub fn split_denomination_readiness_class<'py>(py: Python<'py>) -> PyResult( py: Python<'py>, - gate: &signer_core::CoinSplitGateResult, + gate: &engine_core::CoinSplitGateResult, ) -> PyResult> { let cls = split_denomination_readiness_class(py)?; let kwargs = PyDict::new(py); @@ -84,7 +84,7 @@ pub fn combine_denomination_readiness_class<'py>(py: Python<'py>) -> PyResult( py: Python<'py>, - gate: &signer_core::CoinCombineGateResult, + gate: &engine_core::CoinCombineGateResult, ) -> PyResult> { let cls = combine_denomination_readiness_class(py)?; let kwargs = PyDict::new(py); @@ -96,23 +96,23 @@ pub fn combine_denomination_readiness_to_py<'py>( cls.call((), Some(&kwargs)) } -fn coin_op_kind_from_py(obj: &Bound<'_, PyAny>) -> PyResult { +fn coin_op_kind_from_py(obj: &Bound<'_, PyAny>) -> PyResult { let op_type: String = obj.getattr("op_type")?.extract()?; match op_type.as_str() { - "split" => Ok(signer_core::CoinOpKind::Split), - "combine" => Ok(signer_core::CoinOpKind::Combine), + "split" => Ok(engine_core::CoinOpKind::Split), + "combine" => Ok(engine_core::CoinOpKind::Combine), other => Err(PyValueError::new_err(format!( "invalid coin op type: {other}" ))), } } -pub fn bucket_spec_from_py(obj: &Bound<'_, PyAny>) -> PyResult { +pub fn bucket_spec_from_py(obj: &Bound<'_, PyAny>) -> PyResult { let cls = bucket_spec_class(obj.py())?; if !obj.is_instance(&cls)? { return Err(PyTypeError::new_err("expected BucketSpec")); } - Ok(signer_core::BucketSpec { + Ok(engine_core::BucketSpec { size_base_units: obj.getattr("size_base_units")?.extract()?, target_count: obj.getattr("target_count")?.extract()?, split_buffer_count: obj.getattr("split_buffer_count")?.extract()?, @@ -121,12 +121,12 @@ pub fn bucket_spec_from_py(obj: &Bound<'_, PyAny>) -> PyResult) -> PyResult { +pub fn coin_op_plan_from_py(obj: &Bound<'_, PyAny>) -> PyResult { let cls = coin_op_plan_class(obj.py())?; if !obj.is_instance(&cls)? { return Err(PyTypeError::new_err("expected CoinOpPlan")); } - Ok(signer_core::CoinOpPlan { + Ok(engine_core::CoinOpPlan { op_type: coin_op_kind_from_py(obj)?, size_base_units: obj.getattr("size_base_units")?.extract()?, op_count: obj.getattr("op_count")?.extract()?, @@ -136,7 +136,7 @@ pub fn coin_op_plan_from_py(obj: &Bound<'_, PyAny>) -> PyResult( py: Python<'py>, - plan: &signer_core::CoinOpPlan, + plan: &engine_core::CoinOpPlan, ) -> PyResult> { let cls = coin_op_plan_class(py)?; let kwargs = PyDict::new(py); @@ -149,7 +149,7 @@ pub fn coin_op_plan_to_py<'py>( pub fn coin_op_plans_from_py_list( plans: &Bound<'_, PyList>, -) -> PyResult> { +) -> PyResult> { let mut parsed = Vec::with_capacity(plans.len()); for item in plans.iter() { parsed.push(coin_op_plan_from_py(&item)?); @@ -176,7 +176,7 @@ fn split_skip_plan_class<'py>(py: Python<'py>) -> PyResult> { pub fn spendable_coins_from_py_list( list: &Bound<'_, PyList>, -) -> PyResult> { +) -> PyResult> { let mut coins = Vec::with_capacity(list.len()); for item in list.iter() { let dict = item @@ -201,7 +201,7 @@ pub fn spendable_coins_from_py_list( if amount <= 0 { continue; } - coins.push(signer_core::SpendableCoin { id, amount }); + coins.push(engine_core::SpendableCoin { id, amount }); } Ok(coins) } @@ -273,12 +273,12 @@ pub fn split_auto_select_plan_to_py<'py>( pub fn ladder_target_rows_from_py_list( list: &Bound<'_, PyList>, -) -> PyResult> { +) -> PyResult> { list.iter() .enumerate() .map(|(index, item)| { let label = format!("ladder entry {index}"); - Ok(signer_core::coin_ops::LadderTargetRow { + Ok(engine_core::coin_ops::LadderTargetRow { size_base_units: require_i64_attr(&item, &label, "size_base_units")?, target_count: require_i64_attr(&item, &label, "target_count")?, }) diff --git a/greenfloor-engine-pyo3/src/py_utils/cycle.rs b/greenfloor-engine-pyo3/src/py_utils/cycle.rs index 33e26880..bf3241ad 100644 --- a/greenfloor-engine-pyo3/src/py_utils/cycle.rs +++ b/greenfloor-engine-pyo3/src/py_utils/cycle.rs @@ -73,7 +73,7 @@ pub fn reseed_gap_plan_class<'py>(py: Python<'py>) -> PyResult pub fn reseed_skip_reason_to_py<'py>( py: Python<'py>, - reason: signer_core::ReseedSkipReason, + reason: engine_core::ReseedSkipReason, ) -> PyResult> { let cls = cached_class( py, @@ -86,7 +86,7 @@ pub fn reseed_skip_reason_to_py<'py>( pub fn reseed_gap_plan_to_py<'py>( py: Python<'py>, - plan: &signer_core::ReseedGapPlan, + plan: &engine_core::ReseedGapPlan, ) -> PyResult> { let cls = reseed_gap_plan_class(py)?; let kwargs = PyDict::new(py); @@ -125,7 +125,7 @@ pub fn managed_action_outcome_class<'py>(py: Python<'py>) -> PyResult( py: Python<'py>, - outcome: &signer_core::ManagedActionOutcome, + outcome: &engine_core::ManagedActionOutcome, ) -> PyResult> { let cls = managed_action_outcome_class(py)?; let kwargs = PyDict::new(py); @@ -193,7 +193,7 @@ pub fn cycle_offer_transition_class<'py>(py: Python<'py>) -> PyResult, -) -> PyResult> { +) -> PyResult> { let mut map = BTreeMap::new(); for (asset_id, value) in profiles.iter() { let profile = value @@ -207,7 +207,7 @@ pub fn extract_spendable_profiles( .extract::()?; map.insert( asset_id.extract::()?, - signer_core::SpendableAssetProfile { + engine_core::SpendableAssetProfile { total: profile .get_item("total")? .and_then(|item| item.extract::().ok()) @@ -225,7 +225,7 @@ pub fn extract_spendable_profiles( pub fn strategy_action_sell_counts_from_py_list( list: &Bound<'_, PyList>, -) -> PyResult> { +) -> PyResult> { list.iter() .enumerate() .map(|(index, item)| { @@ -244,7 +244,7 @@ pub fn strategy_action_sell_counts_from_py_list( .map_err(|_| { PyValueError::new_err(format!("{label}.counts_as_executed must be a bool")) })?; - Ok(signer_core::cycle::StrategyActionSellCountInput { + Ok(engine_core::cycle::StrategyActionSellCountInput { size: require_i64_attr(&item, &label, "size")?, side, counts_as_executed, diff --git a/greenfloor-engine-pyo3/src/py_utils/offer_request.rs b/greenfloor-engine-pyo3/src/py_utils/offer_request.rs index 2647cc55..bd83e3de 100644 --- a/greenfloor-engine-pyo3/src/py_utils/offer_request.rs +++ b/greenfloor-engine-pyo3/src/py_utils/offer_request.rs @@ -2,7 +2,7 @@ use std::sync::OnceLock; use pyo3::prelude::*; use pyo3::types::PyDict; -use signer_core::SignerOfferLegAmounts; +use engine_core::SignerOfferLegAmounts; use super::common::cached_class; diff --git a/greenfloor-engine-pyo3/src/py_utils/policy.rs b/greenfloor-engine-pyo3/src/py_utils/policy.rs index 7ddbb5c3..00e5a71c 100644 --- a/greenfloor-engine-pyo3/src/py_utils/policy.rs +++ b/greenfloor-engine-pyo3/src/py_utils/policy.rs @@ -41,7 +41,7 @@ pub fn low_inventory_evaluation_class<'py>(py: Python<'py>) -> PyResult( py: Python<'py>, - decision: &signer_core::CancelPolicyDecision, + decision: &engine_core::CancelPolicyDecision, ) -> PyResult> { let cls = cancel_policy_decision_class(py)?; let kwargs = PyDict::new(py); @@ -55,7 +55,7 @@ pub fn cancel_policy_decision_to_py<'py>( pub fn alert_event_to_py<'py>( py: Python<'py>, - event: &signer_core::AlertEvent, + event: &engine_core::AlertEvent, ) -> PyResult> { let cls = alert_event_class(py)?; let kwargs = PyDict::new(py); @@ -69,7 +69,7 @@ pub fn alert_event_to_py<'py>( pub fn alert_state_to_py<'py>( py: Python<'py>, - state: &signer_core::AlertState, + state: &engine_core::AlertState, ) -> PyResult> { let cls = alert_state_class(py)?; let kwargs = PyDict::new(py); @@ -80,7 +80,7 @@ pub fn alert_state_to_py<'py>( pub fn low_inventory_evaluation_to_py<'py>( py: Python<'py>, - evaluation: &signer_core::LowInventoryEvaluation, + evaluation: &engine_core::LowInventoryEvaluation, ) -> PyResult> { let cls = low_inventory_evaluation_class(py)?; let kwargs = PyDict::new(py); @@ -95,8 +95,8 @@ pub fn low_inventory_evaluation_to_py<'py>( pub fn low_inventory_input_from_py( input: &Bound<'_, PyAny>, -) -> PyResult { - Ok(signer_core::LowInventoryInput { +) -> PyResult { + Ok(engine_core::LowInventoryInput { now_unix: input.getattr("now_unix")?.extract()?, low_inventory_enabled: input.getattr("low_inventory_enabled")?.extract()?, program_default_threshold: input.getattr("program_default_threshold")?.extract()?, diff --git a/greenfloor-engine-pyo3/src/retry_py.rs b/greenfloor-engine-pyo3/src/retry_py.rs index e99e728e..123ad479 100644 --- a/greenfloor-engine-pyo3/src/retry_py.rs +++ b/greenfloor-engine-pyo3/src/retry_py.rs @@ -1,6 +1,6 @@ use pyo3::prelude::*; -use signer_core::{ +use engine_core::{ coinset_fee_lookup_retry_sleep, dexie_invalid_offer_retry_sleep, dexie_invalid_offer_should_retry, moderate_retry_next_sleep, moderate_retry_sleep_seconds, parse_rate_limit_retry_seconds, poll_exponential_advance_sleep, poll_exponential_sleep_now, diff --git a/greenfloor-engine-pyo3/src/strategy_py.rs b/greenfloor-engine-pyo3/src/strategy_py.rs index 97683927..46246028 100644 --- a/greenfloor-engine-pyo3/src/strategy_py.rs +++ b/greenfloor-engine-pyo3/src/strategy_py.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use pyo3::prelude::*; use pyo3::types::{PyDict, PyList}; -use signer_core::{ +use engine_core::{ evaluate_market, evaluate_two_sided_market_actions, plan_reseed_actions_from_gap, reseed_skip_reason_labels, MarketState, PlannedAction, StrategyConfig, }; diff --git a/greenfloor-engine/Cargo.lock b/greenfloor-engine/Cargo.lock index 17a0ed59..f7aa2288 100644 --- a/greenfloor-engine/Cargo.lock +++ b/greenfloor-engine/Cargo.lock @@ -1750,7 +1750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] -name = "greenfloor-signer" +name = "greenfloor-engine" version = "0.1.0" dependencies = [ "anyhow", diff --git a/greenfloor-engine/Cargo.toml b/greenfloor-engine/Cargo.toml index 2a407924..8014ed4b 100644 --- a/greenfloor-engine/Cargo.toml +++ b/greenfloor-engine/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "greenfloor-signer" +name = "greenfloor-engine" version = "0.1.0" edition = "2021" -description = "GreenFloor Rust kernel: vault KMS signing and deterministic daemon policy" +description = "GreenFloor Rust engine: vault KMS signing and deterministic daemon policy" license = "Apache-2.0" [lib] -name = "greenfloor_signer" +name = "greenfloor_engine" path = "src/lib.rs" [[bin]] -name = "greenfloor-signer" +name = "greenfloor-engine" path = "src/main.rs" [dependencies] diff --git a/greenfloor-engine/src/coin_ops/mod.rs b/greenfloor-engine/src/coin_ops/mod.rs index 4573bc3a..e339e00b 100644 --- a/greenfloor-engine/src/coin_ops/mod.rs +++ b/greenfloor-engine/src/coin_ops/mod.rs @@ -1,7 +1,6 @@ //! Deterministic coin-operation policy (planning, fee budget, inventory buckets). //! -//! Lives in the `greenfloor-signer` crate alongside vault signing and cycle policy. -//! See ADR 0010 for the planned crate rename to `greenfloor-kernel`. +//! Lives in the `greenfloor-engine` crate alongside vault signing and cycle policy. mod effective_counts; mod fee_budget; diff --git a/greenfloor-engine/src/cycle/mod.rs b/greenfloor-engine/src/cycle/mod.rs index 1c380bbc..dfd1e769 100644 --- a/greenfloor-engine/src/cycle/mod.rs +++ b/greenfloor-engine/src/cycle/mod.rs @@ -1,6 +1,6 @@ //! Deterministic cycle orchestration, managed dispatch, and offer reconciliation. //! -//! See ADR 0010 for the planned crate rename to `greenfloor-kernel`. +//! Deterministic daemon cycle policy. pub mod cancel; pub mod dispatch; diff --git a/greenfloor-engine/src/lib.rs b/greenfloor-engine/src/lib.rs index 1cf49777..a8b15a50 100644 --- a/greenfloor-engine/src/lib.rs +++ b/greenfloor-engine/src/lib.rs @@ -1,9 +1,7 @@ -//! GreenFloor Rust kernel: vault KMS signing and deterministic daemon policy. +//! GreenFloor Rust engine: vault KMS signing and deterministic daemon policy. //! -//! The Cargo crate is still named `greenfloor-signer` and the PyO3 module is -//! `greenfloor_signer` during the Python migration (ADR 0010). Policy is grouped -//! by domain (`cycle/`, `coin_ops/`, `offer/`, `vault/`) regardless of the legacy -//! crate name; the post-migration target is `greenfloor-kernel`. +//! The Rust crate and PyO3 module are named `greenfloor_engine` (ADR 0010). +//! Policy is grouped by domain (`cycle/`, `coin_ops/`, `offer/`, `vault/`). pub mod bls; pub mod coin_ops; diff --git a/greenfloor-engine/src/main.rs b/greenfloor-engine/src/main.rs index 2075a0cd..a080c8cd 100644 --- a/greenfloor-engine/src/main.rs +++ b/greenfloor-engine/src/main.rs @@ -1,8 +1,8 @@ use std::path::{Path, PathBuf}; use clap::{Parser, Subcommand}; -use greenfloor_signer::vault::members::hex_to_bytes32; -use greenfloor_signer::{ +use greenfloor_engine::vault::members::hex_to_bytes32; +use greenfloor_engine::{ build_and_optionally_broadcast_vault_cat_mixed_split, build_vault_cat_offer, load_signer_config, parse_coin_ids, resolve_vault_context, CreateOfferRequest, MixedSplitRequest, @@ -10,7 +10,7 @@ use greenfloor_signer::{ #[derive(Debug, Parser)] #[command( - name = "greenfloor-signer", + name = "greenfloor-engine", about = "Local Chia vault signing backed by chia-wallet-sdk" )] struct Cli { @@ -91,7 +91,7 @@ async fn main() { } } -async fn run() -> Result<(), greenfloor_signer::Error> { +async fn run() -> Result<(), greenfloor_engine::Error> { let cli = Cli::parse(); match cli.command { Commands::VaultInfo { config, json } => { @@ -101,7 +101,7 @@ async fn run() -> Result<(), greenfloor_signer::Error> { println!( "{}", serde_json::to_string_pretty(&context).map_err(|err| { - greenfloor_signer::Error::Other(format!("json encode failed: {err}")) + greenfloor_engine::Error::Other(format!("json encode failed: {err}")) })? ); } else { @@ -185,7 +185,7 @@ async fn run_mixed_split( coin_ids: Vec, allow_sub_cat_output: bool, broadcast: bool, -) -> Result { +) -> Result { let config = load_signer_config(config_path)?; let parsed_coin_ids = if coin_ids.is_empty() { Vec::new() @@ -207,7 +207,7 @@ async fn run_mixed_split( .await } -fn print_vault_info(context: &greenfloor_signer::vault::VaultContext) { +fn print_vault_info(context: &greenfloor_engine::vault::VaultContext) { println!("network: {}", context.network); println!("launcher_id: {}", context.launcher_id); println!("inner_puzzle_hash: {}", context.inner_puzzle_hash); @@ -232,14 +232,14 @@ fn print_vault_info(context: &greenfloor_signer::vault::VaultContext) { } fn print_mixed_split_result( - result: &greenfloor_signer::MixedSplitResult, + result: &greenfloor_engine::MixedSplitResult, json: bool, -) -> Result<(), greenfloor_signer::Error> { +) -> Result<(), greenfloor_engine::Error> { if json { println!( "{}", serde_json::to_string_pretty(result).map_err(|err| { - greenfloor_signer::Error::Other(format!("json encode failed: {err}")) + greenfloor_engine::Error::Other(format!("json encode failed: {err}")) })? ); return Ok(()); @@ -259,14 +259,14 @@ fn print_mixed_split_result( } fn print_create_offer_result( - result: &greenfloor_signer::CreateOfferResult, + result: &greenfloor_engine::CreateOfferResult, json: bool, -) -> Result<(), greenfloor_signer::Error> { +) -> Result<(), greenfloor_engine::Error> { if json { println!( "{}", serde_json::to_string_pretty(result).map_err(|err| { - greenfloor_signer::Error::Other(format!("json encode failed: {err}")) + greenfloor_engine::Error::Other(format!("json encode failed: {err}")) })? ); return Ok(()); diff --git a/greenfloor-engine/src/offer/mod.rs b/greenfloor-engine/src/offer/mod.rs index 63122718..c4414525 100644 --- a/greenfloor-engine/src/offer/mod.rs +++ b/greenfloor-engine/src/offer/mod.rs @@ -1,6 +1,6 @@ //! Offer build, validation, bootstrap planning, and publish policy. //! -//! See ADR 0010 for the planned crate rename to `greenfloor-kernel`. +//! Offer construction, validation, and deterministic offer policy. pub mod action; pub mod assemble; diff --git a/greenfloor/adapters/bls_cat_coins.py b/greenfloor/adapters/bls_cat_coins.py index dd6900af..a8a7168e 100644 --- a/greenfloor/adapters/bls_cat_coins.py +++ b/greenfloor/adapters/bls_cat_coins.py @@ -1,8 +1,8 @@ -"""BLS CAT coin discovery via the Rust kernel (canonical Rust coinset parse path). +"""BLS CAT coin discovery via the Rust engine (canonical Rust coinset parse path). Returns lightweight stand-in objects with ``.coin`` and ``.info.asset_id`` shaped like ``chia_wallet_sdk`` ``Cat`` instances. They are **not** full SDK ``Cat`` values — callers -that need puzzle parsing or driver spends must use the Rust kernel BLS paths instead. +that need puzzle parsing or driver spends must use the Rust engine BLS paths instead. """ from __future__ import annotations @@ -11,7 +11,7 @@ from dataclasses import dataclass from typing import Any, Protocol, cast, runtime_checkable -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine @runtime_checkable @@ -86,16 +86,16 @@ def _fetch_cat_summaries( receive_address: str, asset_id: str, ) -> list[dict[str, Any]]: - kernel = import_kernel() - raw = kernel.list_bls_cat_coins(network, receive_address, asset_id) + engine = import_engine() + raw = engine.list_bls_cat_coins(network, receive_address, asset_id) if not isinstance(raw, list): return [] return [item for item in raw if isinstance(item, dict)] def _fetch_cat_summaries_by_ids(*, network: str, coin_ids: list[str]) -> list[dict[str, Any]]: - kernel = import_kernel() - raw = kernel.list_bls_cat_coins_by_ids(network, coin_ids) + engine = import_engine() + raw = engine.list_bls_cat_coins_by_ids(network, coin_ids) if not isinstance(raw, list): return [] return [item for item in raw if isinstance(item, dict)] diff --git a/greenfloor/adapters/bls_signing.py b/greenfloor/adapters/bls_signing.py index fefd8c92..7963650b 100644 --- a/greenfloor/adapters/bls_signing.py +++ b/greenfloor/adapters/bls_signing.py @@ -1,14 +1,14 @@ -"""BLS keyring signing: thin Python wrappers over Rust kernel BLS paths. +"""BLS keyring signing: thin Python wrappers over Rust engine BLS paths. Offer, mixed-split, XCH split/combine spend bundles and key loading run in the -``greenfloor-signer`` crate. Vault KMS uses ``greenfloor.adapters.rust_signer``. +``greenfloor-engine`` crate. Vault KMS uses ``greenfloor.adapters.rust_signer``. """ from __future__ import annotations from typing import Any -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine from greenfloor.hex_utils import canonical_is_xch @@ -26,10 +26,10 @@ def _load_master_private_key( ) -> tuple[bytes | None, str | None]: _ = keyring_yaml_path try: - kernel = import_kernel() - result = kernel.load_bls_master_sk(str(key_id).strip()) + engine = import_engine() + result = engine.load_bls_master_sk(str(key_id).strip()) except Exception as exc: - return None, f"greenfloor_kernel_import_error:{exc}" + return None, f"greenfloor_engine_import_error:{exc}" if not isinstance(result, dict): return None, "invalid_load_bls_master_sk_response" error = result.get("error") @@ -48,10 +48,10 @@ def _call_signer_build( request: dict[str, Any], ) -> tuple[str | None, str | None]: try: - kernel = import_kernel() - build = getattr(kernel, method_name) + engine = import_engine() + build = getattr(engine, method_name) except Exception as exc: - return None, f"greenfloor_kernel_import_error:{exc}" + return None, f"greenfloor_engine_import_error:{exc}" try: result = build(network, master_sk_bytes, request) except Exception as exc: @@ -131,12 +131,12 @@ def _build_mixed_split_spend_bundle(payload: dict[str, Any]) -> tuple[str | None def _broadcast_bls_spend_bundle_rust(*, network: str, spend_bundle_hex: str) -> dict[str, Any]: try: - kernel = import_kernel() - result = kernel.broadcast_bls_spend_bundle(network, spend_bundle_hex) + engine = import_engine() + result = engine.broadcast_bls_spend_bundle(network, spend_bundle_hex) except Exception as exc: return { "status": "skipped", - "reason": f"greenfloor_kernel_import_error:{exc}", + "reason": f"greenfloor_engine_import_error:{exc}", "operation_id": None, } if not isinstance(result, dict): diff --git a/greenfloor/adapters/coinset.py b/greenfloor/adapters/coinset.py index 286dc62f..c4b0f152 100644 --- a/greenfloor/adapters/coinset.py +++ b/greenfloor/adapters/coinset.py @@ -1,9 +1,9 @@ -"""Coinset adapter: query helpers in Python, mutations via Rust kernel bindings. +"""Coinset adapter: query helpers in Python, mutations via Rust engine bindings. Read-only Coinset HTTP calls (coin lookups, offer payload parsing) live here and use ``_post_json`` against the configured MSP base URL. Transaction push and fee -estimation for signed spend bundles are delegated to the Rust kernel PyO3 module -(``kernel_bridge.import_kernel``) so mutation paths share the same Rust IO stack. +estimation for signed spend bundles are delegated to the Rust engine PyO3 module +(``engine_bridge.import_engine``) so mutation paths share the same Rust IO stack. """ from __future__ import annotations @@ -14,7 +14,7 @@ import urllib.request from typing import Any -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine from greenfloor.hex_utils import normalize_hex_id _COINSET_TX_ID_KEYS = ( @@ -130,12 +130,12 @@ def _walk(node: object) -> None: def _require_rust_coinset(name: str, *args: Any, **kwargs: Any) -> Any: try: - kernel = import_kernel() + engine = import_engine() except ImportError as exc: - raise RuntimeError(f"greenfloor_kernel_required_for_coinset_io: {exc}") from exc - fn = getattr(kernel, name, None) + raise RuntimeError(f"greenfloor_engine_required_for_coinset_io: {exc}") from exc + fn = getattr(engine, name, None) if not callable(fn): - raise RuntimeError(f"greenfloor_kernel_missing_coinset_fn:{name}") + raise RuntimeError(f"greenfloor_engine_missing_coinset_fn:{name}") return fn(*args, **kwargs) diff --git a/greenfloor/adapters/native_offer.py b/greenfloor/adapters/native_offer.py index 8576f988..d3a69da2 100644 --- a/greenfloor/adapters/native_offer.py +++ b/greenfloor/adapters/native_offer.py @@ -1,15 +1,15 @@ -"""Offer codec helpers via the Rust kernel (``kernel_bridge.import_kernel``).""" +"""Offer codec helpers via the Rust engine (``engine_bridge.import_engine``).""" from __future__ import annotations from typing import Any -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine def encode_offer_from_spend_bundle_hex(raw_hex: str) -> str: - """Encode a spend bundle hex string to offer1... via the Rust kernel.""" - return str(import_kernel().encode_offer(bytes.fromhex(raw_hex))) + """Encode a spend bundle hex string to offer1... via the Rust engine.""" + return str(import_engine().encode_offer(bytes.fromhex(raw_hex))) def _as_bytes(value: Any) -> bytes: @@ -36,14 +36,14 @@ def from_input_spend_bundle_xch( input_spend_bundle: Any, requested_payments_xch: list[Any], ) -> Any: - kernel = import_kernel() + engine = import_engine() requested: list[tuple[bytes, list[tuple[bytes, int]]]] = [] for notarized_payment in requested_payments_xch: payments: list[tuple[bytes, int]] = [] for payment in notarized_payment.payments: payments.append((_as_bytes(payment.puzzle_hash), int(payment.amount))) requested.append((_as_bytes(notarized_payment.nonce), payments)) - spend_bundle_bytes = kernel.from_input_spend_bundle_xch( + spend_bundle_bytes = engine.from_input_spend_bundle_xch( input_spend_bundle.to_bytes(), requested, ) diff --git a/greenfloor/adapters/offer_action.py b/greenfloor/adapters/offer_action.py index 9a2a6836..4a638063 100644 --- a/greenfloor/adapters/offer_action.py +++ b/greenfloor/adapters/offer_action.py @@ -1,8 +1,8 @@ -"""Rust-kernel IO for unified offer-action build.""" +"""Rust-engine IO for unified offer-action build.""" from __future__ import annotations -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine from greenfloor.core.offer_action import ( OfferActionRequest, OfferActionResult, @@ -19,8 +19,8 @@ def build_signer_offer_for_action( config_path: str, request: OfferActionRequest, ) -> OfferActionResult: - kernel = import_kernel() - result = kernel.build_signer_offer_for_action(str(config_path), dict(request)) + engine = import_engine() + result = engine.build_signer_offer_for_action(str(config_path), dict(request)) return parse_action_result(result) @@ -30,6 +30,6 @@ def build_bls_offer_for_action( key_id: str, request: OfferActionRequest, ) -> OfferActionResult: - kernel = import_kernel() - result = kernel.build_bls_offer_for_action_key(str(network), str(key_id), dict(request)) + engine = import_engine() + result = engine.build_bls_offer_for_action_key(str(network), str(key_id), dict(request)) return parse_action_result(result) diff --git a/greenfloor/adapters/rust_signer.py b/greenfloor/adapters/rust_signer.py index d1800a4c..f403ff09 100644 --- a/greenfloor/adapters/rust_signer.py +++ b/greenfloor/adapters/rust_signer.py @@ -1,4 +1,4 @@ -"""Thin wrapper around the Rust kernel vault path (PyO3 module ``greenfloor_kernel``).""" +"""Thin wrapper around the Rust engine vault path (PyO3 module ``greenfloor_engine``).""" from __future__ import annotations @@ -7,7 +7,7 @@ from pathlib import Path from typing import Any -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine from greenfloor.core.signer_offer_request import ( SignerCreateOfferPayload, signer_create_offer_request_from_fields, @@ -15,9 +15,9 @@ def resolve_vault_context(program_path: str) -> dict[str, Any]: - """Load vault display context from program config via the Rust kernel.""" - kernel = import_kernel() - result = kernel.resolve_vault_context(str(program_path)) + """Load vault display context from program config via the Rust engine.""" + engine = import_engine() + result = engine.resolve_vault_context(str(program_path)) if not isinstance(result, dict): raise TypeError("resolve_vault_context returned non-dict result") return result @@ -27,23 +27,23 @@ def build_vault_cat_offer( program_path: str, request_dict: SignerCreateOfferPayload | dict[str, Any], ) -> dict[str, Any]: - """Build a vault CAT offer using the low-level Rust kernel vault path. + """Build a vault CAT offer using the low-level Rust engine vault path. Deprecated for market-action offer creation: use ``greenfloor.adapters.offer_action.build_signer_offer_for_action`` instead. Retained for KMS signing router plan-dict spends and parity tests. """ - kernel = import_kernel() - result = kernel.build_vault_cat_offer(str(program_path), request_dict) + engine = import_engine() + result = engine.build_vault_cat_offer(str(program_path), request_dict) if not isinstance(result, dict): raise TypeError("build_vault_cat_offer returned non-dict result") return result def build_mixed_split(program_path: str, request_dict: dict[str, Any]) -> dict[str, Any]: - """Build (and optionally broadcast) a vault CAT mixed split via the Rust kernel.""" - kernel = import_kernel() - result = kernel.build_mixed_split(str(program_path), request_dict) + """Build (and optionally broadcast) a vault CAT mixed split via the Rust engine.""" + engine = import_engine() + result = engine.build_mixed_split(str(program_path), request_dict) if not isinstance(result, dict): raise TypeError("build_mixed_split returned non-dict result") return result @@ -51,8 +51,8 @@ def build_mixed_split(program_path: str, request_dict: dict[str, Any]) -> dict[s def resolve_offer_asset_ids(program_path: str, base_asset: str, quote_asset: str) -> dict[str, str]: """Resolve market symbols or asset ids to canonical offer asset ids.""" - kernel = import_kernel() - result = kernel.resolve_offer_asset_ids(str(program_path), base_asset, quote_asset) + engine = import_engine() + result = engine.resolve_offer_asset_ids(str(program_path), base_asset, quote_asset) if not isinstance(result, dict): raise TypeError("resolve_offer_asset_ids returned non-dict result") base_asset_id = str(result.get("base_asset_id", "")).strip() @@ -75,7 +75,7 @@ def program_config_path_from_payload(payload: dict[str, Any]) -> str | None: def is_vault_kms_payload(payload: dict[str, Any]) -> bool: - """True when payload should use vault KMS signing (Rust kernel vault path).""" + """True when payload should use vault KMS signing (Rust engine vault path).""" return bool(str(payload.get("signer_kms_key_id", "")).strip()) diff --git a/greenfloor/core/cancel_policy.py b/greenfloor/core/cancel_policy.py index ae72a4dd..a356338f 100644 --- a/greenfloor/core/cancel_policy.py +++ b/greenfloor/core/cancel_policy.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import Any -from greenfloor.core.kernel_bridge import policy_kernel +from greenfloor.core.engine_bridge import policy_engine @dataclass(frozen=True, slots=True) @@ -24,7 +24,7 @@ class OpenOfferRow: def abs_move_bps(current: float | None, previous: float | None) -> float | None: - result = policy_kernel().abs_move_bps(current, previous) + result = policy_engine().abs_move_bps(current, previous) return None if result is None else float(result) @@ -33,7 +33,7 @@ def cancel_move_threshold_bps( market_threshold: int | None = None, env_threshold: int | None = None, ) -> int: - return int(policy_kernel().cancel_move_threshold_bps(market_threshold, env_threshold)) + return int(policy_engine().cancel_move_threshold_bps(market_threshold, env_threshold)) def evaluate_cancel_policy_decision( @@ -45,7 +45,7 @@ def evaluate_cancel_policy_decision( market_threshold: int | None = None, env_threshold: int | None = None, ) -> CancelPolicyDecision: - result = policy_kernel().evaluate_cancel_policy_decision( + result = policy_engine().evaluate_cancel_policy_decision( str(quote_asset_type), bool(cancel_policy_stable_vs_unstable), current_xch_price_usd, @@ -71,7 +71,7 @@ def open_offer_rows_from_dicts(offers: list[dict[str, Any]]) -> list[OpenOfferRo def collect_open_offer_ids_for_cancel(offers: list[OpenOfferRow]) -> list[str]: - result = policy_kernel().collect_open_offer_ids_for_cancel(offers) + result = policy_engine().collect_open_offer_ids_for_cancel(offers) if not isinstance(result, list): raise TypeError("collect_open_offer_ids_for_cancel returned non-list result") return [str(offer_id) for offer_id in result] diff --git a/greenfloor/core/coin_ops/__init__.py b/greenfloor/core/coin_ops/__init__.py index 8225f208..e2cce155 100644 --- a/greenfloor/core/coin_ops/__init__.py +++ b/greenfloor/core/coin_ops/__init__.py @@ -1,4 +1,4 @@ -"""Coin-operation deterministic policy (Rust-backed kernel). +"""Coin-operation deterministic policy (Rust-backed engine). ``SplitPlanningProfile`` controls ``plan_auto_split_selection()``: diff --git a/greenfloor/core/coin_ops/_bridge.py b/greenfloor/core/coin_ops/_bridge.py index 7cd0ede0..1dac4bfc 100644 --- a/greenfloor/core/coin_ops/_bridge.py +++ b/greenfloor/core/coin_ops/_bridge.py @@ -2,14 +2,14 @@ Each public function is an explicit FFI wrapper (coercion, validation, docstrings). Do not collapse these into a generic dispatch helper; the repetition is intentional so -each kernel call stays auditable at the Python boundary (see progress.md step 10 handoff). +each engine call stays auditable at the Python boundary (see progress.md step 10 handoff). """ from __future__ import annotations from typing import Any -from greenfloor.core import kernel_bridge +from greenfloor.core import engine_bridge from greenfloor.core.coin_ops.selection_bridge import ( plan_auto_combine_inputs as _plan_auto_combine_inputs, ) @@ -31,16 +31,16 @@ SplitDenominationReadiness, SplitPlanningProfile, ) -from greenfloor.core.kernel_maps import require_i64_i64_map +from greenfloor.core.engine_maps import require_i64_i64_map def _require_coin_op_plans(value: object) -> list[CoinOpPlan]: if not isinstance(value, list): - raise TypeError("kernel returned non-list result") + raise TypeError("engine returned non-list result") plans: list[CoinOpPlan] = [] for item in value: if not isinstance(item, CoinOpPlan): - raise TypeError("kernel returned non-CoinOpPlan result") + raise TypeError("engine returned non-CoinOpPlan result") plans.append(item) return plans @@ -54,7 +54,7 @@ def plan_coin_ops( combine_fee_mojos: int, ) -> list[CoinOpPlan]: return _require_coin_op_plans( - kernel_bridge.coin_ops_kernel().plan_coin_ops( + engine_bridge.coin_ops_engine().plan_coin_ops( buckets, int(max_operations_per_run), int(max_fee_budget_mojos), @@ -71,7 +71,7 @@ def projected_coin_ops_fee_mojos( combine_fee_mojos: int, ) -> int: return int( - kernel_bridge.coin_ops_kernel().projected_coin_ops_fee_mojos( + engine_bridge.coin_ops_engine().projected_coin_ops_fee_mojos( plans, int(split_fee_mojos), int(combine_fee_mojos), @@ -86,7 +86,7 @@ def fee_budget_allows_execution( projected_mojos: int, ) -> bool: return bool( - kernel_bridge.coin_ops_kernel().fee_budget_allows_execution( + engine_bridge.coin_ops_engine().fee_budget_allows_execution( int(max_daily_fee_budget_mojos), int(spent_today_mojos), int(projected_mojos), @@ -107,7 +107,7 @@ def partition_plans_by_budget( Preserves input order. If budget is unlimited (<=0), all plans are executable. Can split a plan by op_count if only partial operations fit. """ - allowed, skipped = kernel_bridge.coin_ops_kernel().partition_plans_by_budget( + allowed, skipped = engine_bridge.coin_ops_engine().partition_plans_by_budget( plans, int(split_fee_mojos), int(combine_fee_mojos), @@ -126,7 +126,7 @@ def compute_bucket_counts_from_coins( V1 logic is exact-match by ladder size to keep behavior deterministic and auditable. """ - raw = kernel_bridge.coin_ops_kernel().compute_bucket_counts_from_coins( + raw = engine_bridge.coin_ops_engine().compute_bucket_counts_from_coins( [int(amount) for amount in coin_amounts_base_units], [int(size) for size in ladder_sizes], ) @@ -138,18 +138,18 @@ def coin_op_min_amount_mojos(*, canonical_asset_id: str) -> int: # bug documented in docs/ent-wallet-upstream-byc-coin-query-issue.md. # Ignore sub-1-CAT dust during local split/combine candidate selection so # tiny stray rows do not get pulled into operational coin management. - return int(kernel_bridge.coin_ops_kernel().coin_op_min_amount_mojos(str(canonical_asset_id))) + return int(engine_bridge.coin_ops_engine().coin_op_min_amount_mojos(str(canonical_asset_id))) def coin_meets_coin_op_min_amount(coin: dict, *, canonical_asset_id: str) -> bool: return bool( - kernel_bridge.coin_ops_kernel().coin_meets_coin_op_min_amount(coin, str(canonical_asset_id)) + engine_bridge.coin_ops_engine().coin_meets_coin_op_min_amount(coin, str(canonical_asset_id)) ) def coin_op_target_amount_allowed(*, amount_mojos: int, canonical_asset_id: str) -> bool: return bool( - kernel_bridge.coin_ops_kernel().coin_op_target_amount_allowed( + engine_bridge.coin_ops_engine().coin_op_target_amount_allowed( int(amount_mojos), str(canonical_asset_id), ) @@ -162,7 +162,7 @@ def select_spendable_coins_for_target_amount( target_amount: int, ) -> tuple[list[str], int, bool]: return _select_spendable_coins_for_target_amount( - kernel_bridge.coin_ops_kernel(), + engine_bridge.coin_ops_engine(), coins=coins, target_amount=target_amount, ) @@ -175,7 +175,7 @@ def split_would_create_sub_cat_change( canonical_asset_id: str, ) -> tuple[bool, int]: return _split_would_create_sub_cat_change( - kernel_bridge.coin_ops_kernel(), + engine_bridge.coin_ops_engine(), selected_amount_mojos=selected_amount_mojos, required_amount_mojos=required_amount_mojos, canonical_asset_id=canonical_asset_id, @@ -192,7 +192,7 @@ def plan_auto_split_selection( allow_combine_prereq: bool | None = None, ) -> SplitAutoSelectPlan: return _plan_auto_split_selection( - kernel_bridge.coin_ops_kernel(), + engine_bridge.coin_ops_engine(), candidate_spendable=candidate_spendable, required_amount_mojos=required_amount_mojos, canonical_asset_id=canonical_asset_id, @@ -212,7 +212,7 @@ def plan_auto_combine_inputs( max_count: int | None = None, ) -> list[str]: return _plan_auto_combine_inputs( - kernel_bridge.coin_ops_kernel(), + engine_bridge.coin_ops_engine(), spendable_coins=spendable_coins, number_of_coins=number_of_coins, selection_mode=selection_mode, @@ -223,7 +223,7 @@ def plan_auto_combine_inputs( def is_spendable_wallet_coin(coin: dict[str, Any]) -> bool: - return bool(kernel_bridge.coin_ops_kernel().is_spendable_wallet_coin(coin)) + return bool(engine_bridge.coin_ops_engine().is_spendable_wallet_coin(coin)) def evaluate_coin_split_gate( @@ -233,14 +233,14 @@ def evaluate_coin_split_gate( size_base_units: int, required_count: int, ) -> SplitDenominationReadiness: - result = kernel_bridge.coin_ops_kernel().evaluate_coin_split_gate( + result = engine_bridge.coin_ops_engine().evaluate_coin_split_gate( asset_scoped_coins, str(resolved_asset_id), int(size_base_units), int(required_count), ) if not isinstance(result, SplitDenominationReadiness): - raise TypeError("kernel returned non-SplitDenominationReadiness result") + raise TypeError("engine returned non-SplitDenominationReadiness result") return result @@ -251,14 +251,14 @@ def evaluate_coin_combine_gate( size_base_units: int, max_allowed_count: int, ) -> CombineDenominationReadiness: - result = kernel_bridge.coin_ops_kernel().evaluate_coin_combine_gate( + result = engine_bridge.coin_ops_engine().evaluate_coin_combine_gate( asset_scoped_coins, str(asset_id), int(size_base_units), int(max_allowed_count), ) if not isinstance(result, CombineDenominationReadiness): - raise TypeError("kernel returned non-CombineDenominationReadiness result") + raise TypeError("engine returned non-CombineDenominationReadiness result") return result @@ -270,7 +270,7 @@ def coin_op_should_stop( iteration: int, max_iterations: int, ) -> tuple[bool, str]: - stop, reason = kernel_bridge.coin_ops_kernel().coin_op_should_stop( + stop, reason = engine_bridge.coin_ops_engine().coin_op_should_stop( bool(until_ready), readiness_ready, bool(coin_ids), @@ -288,7 +288,7 @@ def effective_sell_bucket_counts_for_coin_ops( newly_executed_sell_offer_counts_by_size: dict[int, int] | None = None, ) -> dict[int, int]: return require_i64_i64_map( - kernel_bridge.coin_ops_kernel().effective_sell_bucket_counts_for_coin_ops( + engine_bridge.coin_ops_engine().effective_sell_bucket_counts_for_coin_ops( sell_ladder, wallet_bucket_counts, active_sell_offer_counts_by_size, diff --git a/greenfloor/core/coin_ops/engine_protocol.py b/greenfloor/core/coin_ops/engine_protocol.py new file mode 100644 index 00000000..1d4de281 --- /dev/null +++ b/greenfloor/core/coin_ops/engine_protocol.py @@ -0,0 +1,131 @@ +"""Typed PyO3 surface for coin-operation policy bindings.""" + +from __future__ import annotations + +from typing import Any, Protocol + +from greenfloor.core.coin_ops.types import ( + BucketSpec, + CoinOpPlan, + CombineDenominationReadiness, + CombineInputSelectionMode, + SplitAutoSelectPlan, + SplitDenominationReadiness, + SplitPlanningProfile, +) + + +class CoinOpsEngineProtocol(Protocol): + def plan_coin_ops( + self, + buckets: list[BucketSpec], + max_operations_per_run: int, + max_fee_budget_mojos: int, + split_fee_mojos: int, + combine_fee_mojos: int, + ) -> list[CoinOpPlan]: ... + + def projected_coin_ops_fee_mojos( + self, + plans: list[CoinOpPlan], + split_fee_mojos: int, + combine_fee_mojos: int, + ) -> int: ... + + def fee_budget_allows_execution( + self, + max_daily_fee_budget_mojos: int, + spent_today_mojos: int, + projected_mojos: int, + ) -> bool: ... + + def partition_plans_by_budget( + self, + plans: list[CoinOpPlan], + split_fee_mojos: int, + combine_fee_mojos: int, + spent_today_mojos: int, + max_daily_fee_budget_mojos: int, + ) -> tuple[list[CoinOpPlan], list[CoinOpPlan]]: ... + + def compute_bucket_counts_from_coins( + self, + coin_amounts_base_units: list[int], + ladder_sizes: list[int], + ) -> dict[int, int]: ... + + def coin_op_min_amount_mojos(self, canonical_asset_id: str) -> int: ... + + def coin_meets_coin_op_min_amount( + self, coin: dict[str, object], canonical_asset_id: str + ) -> bool: ... + + def coin_op_target_amount_allowed(self, amount_mojos: int, canonical_asset_id: str) -> bool: ... + + def select_spendable_coins_for_target_amount( + self, + coins: list[dict[str, object]], + target_amount: int, + ) -> tuple[list[str], int, bool]: ... + + def split_would_create_sub_cat_change( + self, + selected_amount_mojos: int, + required_amount_mojos: int, + canonical_asset_id: str, + ) -> tuple[bool, int]: ... + + def plan_auto_split_selection( + self, + candidate_spendable: list[dict[str, object]], + required_amount_mojos: int, + canonical_asset_id: str, + profile: SplitPlanningProfile, + combine_input_cap: int, + allow_combine_prereq: bool | None, + ) -> SplitAutoSelectPlan: ... + + def plan_auto_combine_inputs( + self, + spendable_coins: list[dict[str, object]], + number_of_coins: int, + selection_mode: CombineInputSelectionMode, + target_amount_mojos: int | None, + exclude_coin_ids: set[str] | None, + max_count: int | None, + ) -> list[str]: ... + + def is_spendable_wallet_coin(self, coin: dict[str, Any]) -> bool: ... + + def evaluate_coin_split_gate( + self, + asset_scoped_coins: list[dict[str, Any]], + resolved_asset_id: str, + size_base_units: int, + required_count: int, + ) -> SplitDenominationReadiness: ... + + def evaluate_coin_combine_gate( + self, + asset_scoped_coins: list[dict[str, Any]], + asset_id: str, + size_base_units: int, + max_allowed_count: int, + ) -> CombineDenominationReadiness: ... + + def coin_op_should_stop( + self, + until_ready: bool, + final_readiness_ready: bool | None, + has_explicit_coin_ids: bool, + iteration: int, + max_iterations: int, + ) -> tuple[bool, str]: ... + + def effective_sell_bucket_counts_for_coin_ops( + self, + sell_ladder: list[Any], + wallet_bucket_counts: dict[int, int], + active_sell_offer_counts_by_size: dict[int, int] | None, + newly_executed_sell_offer_counts_by_size: dict[int, int] | None, + ) -> dict[int, int]: ... diff --git a/greenfloor/core/coin_ops/kernel_protocol.py b/greenfloor/core/coin_ops/kernel_protocol.py index 044e7645..d31eb02f 100644 --- a/greenfloor/core/coin_ops/kernel_protocol.py +++ b/greenfloor/core/coin_ops/kernel_protocol.py @@ -1,131 +1,7 @@ -"""Typed PyO3 surface for coin-operation policy bindings.""" +"""Compatibility shim for coin-op engine protocol names.""" -from __future__ import annotations +from greenfloor.core.coin_ops.engine_protocol import CoinOpsEngineProtocol -from typing import Any, Protocol +CoinOpsKernelProtocol = CoinOpsEngineProtocol -from greenfloor.core.coin_ops.types import ( - BucketSpec, - CoinOpPlan, - CombineDenominationReadiness, - CombineInputSelectionMode, - SplitAutoSelectPlan, - SplitDenominationReadiness, - SplitPlanningProfile, -) - - -class CoinOpsKernelProtocol(Protocol): - def plan_coin_ops( - self, - buckets: list[BucketSpec], - max_operations_per_run: int, - max_fee_budget_mojos: int, - split_fee_mojos: int, - combine_fee_mojos: int, - ) -> list[CoinOpPlan]: ... - - def projected_coin_ops_fee_mojos( - self, - plans: list[CoinOpPlan], - split_fee_mojos: int, - combine_fee_mojos: int, - ) -> int: ... - - def fee_budget_allows_execution( - self, - max_daily_fee_budget_mojos: int, - spent_today_mojos: int, - projected_mojos: int, - ) -> bool: ... - - def partition_plans_by_budget( - self, - plans: list[CoinOpPlan], - split_fee_mojos: int, - combine_fee_mojos: int, - spent_today_mojos: int, - max_daily_fee_budget_mojos: int, - ) -> tuple[list[CoinOpPlan], list[CoinOpPlan]]: ... - - def compute_bucket_counts_from_coins( - self, - coin_amounts_base_units: list[int], - ladder_sizes: list[int], - ) -> dict[int, int]: ... - - def coin_op_min_amount_mojos(self, canonical_asset_id: str) -> int: ... - - def coin_meets_coin_op_min_amount( - self, coin: dict[str, object], canonical_asset_id: str - ) -> bool: ... - - def coin_op_target_amount_allowed(self, amount_mojos: int, canonical_asset_id: str) -> bool: ... - - def select_spendable_coins_for_target_amount( - self, - coins: list[dict[str, object]], - target_amount: int, - ) -> tuple[list[str], int, bool]: ... - - def split_would_create_sub_cat_change( - self, - selected_amount_mojos: int, - required_amount_mojos: int, - canonical_asset_id: str, - ) -> tuple[bool, int]: ... - - def plan_auto_split_selection( - self, - candidate_spendable: list[dict[str, object]], - required_amount_mojos: int, - canonical_asset_id: str, - profile: SplitPlanningProfile, - combine_input_cap: int, - allow_combine_prereq: bool | None, - ) -> SplitAutoSelectPlan: ... - - def plan_auto_combine_inputs( - self, - spendable_coins: list[dict[str, object]], - number_of_coins: int, - selection_mode: CombineInputSelectionMode, - target_amount_mojos: int | None, - exclude_coin_ids: set[str] | None, - max_count: int | None, - ) -> list[str]: ... - - def is_spendable_wallet_coin(self, coin: dict[str, Any]) -> bool: ... - - def evaluate_coin_split_gate( - self, - asset_scoped_coins: list[dict[str, Any]], - resolved_asset_id: str, - size_base_units: int, - required_count: int, - ) -> SplitDenominationReadiness: ... - - def evaluate_coin_combine_gate( - self, - asset_scoped_coins: list[dict[str, Any]], - asset_id: str, - size_base_units: int, - max_allowed_count: int, - ) -> CombineDenominationReadiness: ... - - def coin_op_should_stop( - self, - until_ready: bool, - final_readiness_ready: bool | None, - has_explicit_coin_ids: bool, - iteration: int, - max_iterations: int, - ) -> tuple[bool, str]: ... - - def effective_sell_bucket_counts_for_coin_ops( - self, - sell_ladder: list[Any], - wallet_bucket_counts: dict[int, int], - active_sell_offer_counts_by_size: dict[int, int] | None, - newly_executed_sell_offer_counts_by_size: dict[int, int] | None, - ) -> dict[int, int]: ... +__all__ = ["CoinOpsKernelProtocol"] diff --git a/greenfloor/core/coin_ops/selection_bridge.py b/greenfloor/core/coin_ops/selection_bridge.py index f1280621..f35266be 100644 --- a/greenfloor/core/coin_ops/selection_bridge.py +++ b/greenfloor/core/coin_ops/selection_bridge.py @@ -2,7 +2,7 @@ from __future__ import annotations -from greenfloor.core.coin_ops.kernel_protocol import CoinOpsKernelProtocol +from greenfloor.core.coin_ops.engine_protocol import CoinOpsEngineProtocol from greenfloor.core.coin_ops.types import ( CombineInputSelectionMode, SplitAutoSelectPlan, @@ -16,16 +16,16 @@ def _require_split_auto_select_plan(value: object) -> SplitAutoSelectPlan: if isinstance(value, SplitCoinPlan | SplitCombinePrereqPlan | SplitSkipPlan): return value - raise TypeError("kernel returned invalid split auto-select plan") + raise TypeError("engine returned invalid split auto-select plan") def select_spendable_coins_for_target_amount( - kernel: CoinOpsKernelProtocol, + engine: CoinOpsEngineProtocol, *, coins: list[dict], target_amount: int, ) -> tuple[list[str], int, bool]: - coin_ids, total, exact = kernel.select_spendable_coins_for_target_amount( + coin_ids, total, exact = engine.select_spendable_coins_for_target_amount( coins, int(target_amount), ) @@ -33,13 +33,13 @@ def select_spendable_coins_for_target_amount( def split_would_create_sub_cat_change( - kernel: CoinOpsKernelProtocol, + engine: CoinOpsEngineProtocol, *, selected_amount_mojos: int, required_amount_mojos: int, canonical_asset_id: str, ) -> tuple[bool, int]: - would_create, remainder = kernel.split_would_create_sub_cat_change( + would_create, remainder = engine.split_would_create_sub_cat_change( int(selected_amount_mojos), int(required_amount_mojos), str(canonical_asset_id), @@ -48,7 +48,7 @@ def split_would_create_sub_cat_change( def plan_auto_split_selection( - kernel: CoinOpsKernelProtocol, + engine: CoinOpsEngineProtocol, *, candidate_spendable: list[dict], required_amount_mojos: int, @@ -58,7 +58,7 @@ def plan_auto_split_selection( allow_combine_prereq: bool | None = None, ) -> SplitAutoSelectPlan: return _require_split_auto_select_plan( - kernel.plan_auto_split_selection( + engine.plan_auto_split_selection( candidate_spendable, int(required_amount_mojos), str(canonical_asset_id), @@ -70,7 +70,7 @@ def plan_auto_split_selection( def plan_auto_combine_inputs( - kernel: CoinOpsKernelProtocol, + engine: CoinOpsEngineProtocol, *, spendable_coins: list[dict], number_of_coins: int, @@ -81,7 +81,7 @@ def plan_auto_combine_inputs( ) -> list[str]: return [ str(coin_id) - for coin_id in kernel.plan_auto_combine_inputs( + for coin_id in engine.plan_auto_combine_inputs( spendable_coins, int(number_of_coins), selection_mode, diff --git a/greenfloor/core/cycle/__init__.py b/greenfloor/core/cycle/__init__.py index 779996df..8429dc94 100644 --- a/greenfloor/core/cycle/__init__.py +++ b/greenfloor/core/cycle/__init__.py @@ -54,11 +54,11 @@ size_counts_to_signer, ) from greenfloor.core.cycle_reseed import ReseedGapPlan, ReseedSkipReason -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine def market_cycle_phases() -> tuple[str, ...]: - return tuple(import_kernel().market_cycle_phases()) + return tuple(import_engine().market_cycle_phases()) MARKET_CYCLE_PHASES: tuple[str, ...] = market_cycle_phases() diff --git a/greenfloor/core/cycle/_bridge_managed.py b/greenfloor/core/cycle/_bridge_managed.py index f007ea4d..b4a9bbc8 100644 --- a/greenfloor/core/cycle/_bridge_managed.py +++ b/greenfloor/core/cycle/_bridge_managed.py @@ -3,7 +3,7 @@ from __future__ import annotations from greenfloor.core.cycle._bridge_common import normalize_spendable_profiles -from greenfloor.core.kernel_bridge import policy_kernel +from greenfloor.core.engine_bridge import policy_engine from greenfloor.core.managed_action_outcome import ManagedActionOutcome from greenfloor.core.managed_retry import ManagedRetryDecision from greenfloor.core.parallel_batch_plan import ParallelBatchPlan @@ -41,7 +41,7 @@ def sequential_action_route( managed_backend_available: bool, ) -> str: return str( - policy_kernel().sequential_action_route( + policy_engine().sequential_action_route( bool(runtime_dry_run), bool(program_present), bool(managed_backend_available), @@ -50,14 +50,14 @@ def sequential_action_route( def expand_planned_actions(actions: list[PlannedAction]) -> list[PlannedAction]: - signer = policy_kernel() + signer = policy_engine() return planned_actions_from_signer_list(signer.expand_planned_actions(actions)) def filter_planned_actions_with_positive_repeat( actions: list[PlannedAction], ) -> list[PlannedAction]: - signer = policy_kernel() + signer = policy_engine() return planned_actions_from_signer_list( signer.filter_planned_actions_with_positive_repeat(actions) ) @@ -69,7 +69,7 @@ def plan_parallel_managed_dispatch( ctx: ParallelReservationContext, spendable_profiles: dict[str, dict[str, int | bool]], ) -> ParallelBatchPlan: - signer = policy_kernel() + signer = policy_engine() result = signer.plan_parallel_managed_dispatch( actions, ctx, @@ -85,7 +85,7 @@ def single_input_preferred_skip_reason( requested_amounts: dict[str, int], spendable_profiles: dict[str, dict[str, int | bool]], ) -> str | None: - signer = policy_kernel() + signer = policy_engine() return signer.single_input_preferred_skip_reason( requested_amounts, normalize_spendable_profiles(spendable_profiles), @@ -93,27 +93,27 @@ def single_input_preferred_skip_reason( def is_transient_managed_upstream_error_text(error_text: str) -> bool: - return bool(policy_kernel().is_transient_managed_upstream_error_text(error_text)) + return bool(policy_engine().is_transient_managed_upstream_error_text(error_text)) def classify_managed_transient_error(*, exception_class: str, error_text: str) -> str | None: - return policy_kernel().classify_managed_transient_error(exception_class, error_text) + return policy_engine().classify_managed_transient_error(exception_class, error_text) def is_managed_upstream_transient_error(*, exception_class: str, error_text: str) -> bool: - return bool(policy_kernel().is_managed_upstream_transient_error(exception_class, error_text)) + return bool(policy_engine().is_managed_upstream_transient_error(exception_class, error_text)) def is_managed_worker_transient_error(*, exception_class: str, error_text: str) -> bool: - return bool(policy_kernel().is_managed_worker_transient_error(exception_class, error_text)) + return bool(policy_engine().is_managed_worker_transient_error(exception_class, error_text)) def is_parallel_dispatch_transient_error(*, exception_class: str, error_text: str) -> bool: - return bool(policy_kernel().is_parallel_dispatch_transient_error(exception_class, error_text)) + return bool(policy_engine().is_parallel_dispatch_transient_error(exception_class, error_text)) def is_transient_dexie_visibility_404_error(error: str) -> bool: - return bool(policy_kernel().is_transient_dexie_visibility_404_error(error)) + return bool(policy_engine().is_transient_dexie_visibility_404_error(error)) def can_parallelize_managed_offers( @@ -124,7 +124,7 @@ def can_parallelize_managed_offers( has_coordinator: bool, ) -> bool: return bool( - policy_kernel().can_parallelize_managed_offers( + policy_engine().can_parallelize_managed_offers( signer_path_configured, parallelism_enabled, runtime_dry_run, @@ -134,11 +134,11 @@ def can_parallelize_managed_offers( def parallel_max_workers(*, submission_count: int, configured_max: int) -> int: - return int(policy_kernel().parallel_max_workers(int(submission_count), int(configured_max))) + return int(policy_engine().parallel_max_workers(int(submission_count), int(configured_max))) def reservation_release_status(*, is_executed: bool) -> str: - return str(policy_kernel().reservation_release_status(bool(is_executed))) + return str(policy_engine().reservation_release_status(bool(is_executed))) def should_apply_parallel_transient_cooldown( @@ -148,7 +148,7 @@ def should_apply_parallel_transient_cooldown( cooldown_seconds: int, ) -> bool: return bool( - policy_kernel().should_apply_parallel_transient_cooldown( + policy_engine().should_apply_parallel_transient_cooldown( int(transient_failures), int(total_parallel), int(cooldown_seconds), @@ -163,7 +163,7 @@ def managed_retry_decision( backoff_ms: int, is_upstream_transient: bool, ) -> ManagedRetryDecision: - signer = policy_kernel() + signer = policy_engine() result = signer.managed_retry_decision( int(attempt_index), int(attempts_max), @@ -182,7 +182,7 @@ def classify_managed_post_result( offer_id: str, publish_venue: str, ) -> ManagedActionOutcome: - signer = policy_kernel() + signer = policy_engine() result = signer.classify_managed_post_result(success, error_text, offer_id, publish_venue) if not isinstance(result, ManagedActionOutcome): raise TypeError("classify_managed_post_result returned non-ManagedActionOutcome result") @@ -194,7 +194,7 @@ def classify_dexie_visibility_outcome( visible: bool, visibility_error: str, ) -> ManagedActionOutcome: - signer = policy_kernel() + signer = policy_engine() result = signer.classify_dexie_visibility_outcome(visible, visibility_error) if not isinstance(result, ManagedActionOutcome): raise TypeError( @@ -210,4 +210,4 @@ def count_parallel_transient_failures(items: list[StrategyActionItem]) -> int: f"parallel outcome list item {index} must be StrategyActionItem, " f"got {type(item).__name__}" ) - return int(policy_kernel().count_parallel_transient_failures(items)) + return int(policy_engine().count_parallel_transient_failures(items)) diff --git a/greenfloor/core/cycle/_bridge_orchestration.py b/greenfloor/core/cycle/_bridge_orchestration.py index 94e31e96..52493aca 100644 --- a/greenfloor/core/cycle/_bridge_orchestration.py +++ b/greenfloor/core/cycle/_bridge_orchestration.py @@ -11,8 +11,8 @@ StaleSweepHit, StaleSweepProgress, ) -from greenfloor.core.kernel_bridge import policy_kernel -from greenfloor.core.kernel_maps import require_i64_i64_map, require_side_offer_count_maps +from greenfloor.core.engine_bridge import policy_engine +from greenfloor.core.engine_maps import require_i64_i64_map, require_side_offer_count_maps from greenfloor.core.planned_action import PlannedAction, planned_actions_from_signer_list __all__ = [ @@ -44,7 +44,7 @@ def evaluate_market(*, state: Any, config: Any) -> list[PlannedAction]: - signer = policy_kernel() + signer = policy_engine() return planned_actions_from_signer_list(signer.evaluate_market(state, config)) @@ -55,7 +55,7 @@ def evaluate_two_sided_market_actions( buy_config: Any, sell_config: Any, ) -> list[PlannedAction]: - signer = policy_kernel() + signer = policy_engine() return planned_actions_from_signer_list( signer.evaluate_two_sided_market_actions( buy_state, @@ -67,7 +67,7 @@ def evaluate_two_sided_market_actions( def reseed_skip_reason_labels() -> tuple[str, ...]: - return tuple(str(label) for label in policy_kernel().reseed_skip_reason_labels()) + return tuple(str(label) for label in policy_engine().reseed_skip_reason_labels()) def plan_reseed_actions_from_gap( @@ -78,7 +78,7 @@ def plan_reseed_actions_from_gap( strategy_config: Any, xch_price_usd: float | None, ) -> Any: - signer = policy_kernel() + signer = policy_engine() return signer.plan_reseed_actions_from_gap( strategy_actions, active_counts_by_size, @@ -89,7 +89,7 @@ def plan_reseed_actions_from_gap( def apply_offer_signal(*, state: str, signal: str) -> dict[str, Any]: - signer = policy_kernel() + signer = policy_engine() result = signer.apply_offer_signal(state, signal) if not isinstance(result, dict): raise TypeError("apply_offer_signal returned non-dict result") @@ -97,7 +97,7 @@ def apply_offer_signal(*, state: str, signal: str) -> dict[str, Any]: def expiry_seconds_for_action(*, expiry_unit: str, expiry_value: int) -> int | None: - signer = policy_kernel() + signer = policy_engine() return signer.expiry_seconds_for_action(expiry_unit, expiry_value) @@ -108,7 +108,7 @@ def select_market_batch( cursor: int, immediate_requeue_ids: list[str], ) -> MarketBatchSelection: - signer = policy_kernel() + signer = policy_engine() result = signer.select_market_batch( enabled_market_ids, int(slot_count), @@ -124,12 +124,12 @@ def enqueue_immediate_requeue( immediate_requeue_ids: list[str], market_id: str, ) -> list[str]: - return list(policy_kernel().enqueue_immediate_requeue(immediate_requeue_ids, market_id)) + return list(policy_engine().enqueue_immediate_requeue(immediate_requeue_ids, market_id)) def should_use_market_slot_dispatch(*, enabled_market_count: int, slot_count: int) -> bool: return bool( - policy_kernel().should_use_market_slot_dispatch( + policy_engine().should_use_market_slot_dispatch( int(enabled_market_count), int(slot_count), ) @@ -137,18 +137,18 @@ def should_use_market_slot_dispatch(*, enabled_market_count: int, slot_count: in def dedupe_sorted_market_ids(market_ids: list[str]) -> list[str]: - return list(policy_kernel().dedupe_sorted_market_ids(market_ids)) + return list(policy_engine().dedupe_sorted_market_ids(market_ids)) def should_log_disabled_market(*, now_monotonic: float, next_log_deadline: float) -> bool: return bool( - policy_kernel().should_log_disabled_market(float(now_monotonic), float(next_log_deadline)) + policy_engine().should_log_disabled_market(float(now_monotonic), float(next_log_deadline)) ) def next_disabled_market_log_deadline(*, now_monotonic: float, interval_seconds: int) -> float: return float( - policy_kernel().next_disabled_market_log_deadline( + policy_engine().next_disabled_market_log_deadline( float(now_monotonic), int(interval_seconds), ) @@ -157,7 +157,7 @@ def next_disabled_market_log_deadline(*, now_monotonic: float, interval_seconds: def should_try_cat_inventory_fallback(*, coinset_scan_empty: bool, base_asset: str) -> bool: return bool( - policy_kernel().should_try_cat_inventory_fallback(bool(coinset_scan_empty), base_asset) + policy_engine().should_try_cat_inventory_fallback(bool(coinset_scan_empty), base_asset) ) @@ -167,7 +167,7 @@ def collect_stale_sweep_candidates( enabled_market_ids: list[str], per_market_limit: int, ) -> list[StaleSweepCandidate]: - signer = policy_kernel() + signer = policy_engine() result = signer.collect_stale_sweep_candidates(rows, enabled_market_ids, int(per_market_limit)) if not isinstance(result, list): raise TypeError("collect_stale_sweep_candidates returned non-list result") @@ -181,11 +181,11 @@ def collect_stale_sweep_candidates( def classify_dexie_stale_offer_status(status: int) -> str | None: - return policy_kernel().classify_dexie_stale_offer_status(int(status)) + return policy_engine().classify_dexie_stale_offer_status(int(status)) def is_dexie_offer_missing_error_text(error_text: str) -> bool: - return bool(policy_kernel().is_dexie_offer_missing_error_text(error_text)) + return bool(policy_engine().is_dexie_offer_missing_error_text(error_text)) def record_stale_sweep_check( @@ -193,7 +193,7 @@ def record_stale_sweep_check( progress: StaleSweepProgress, hit: StaleSweepHit | None, ) -> StaleSweepProgress: - signer = policy_kernel() + signer = policy_engine() result = signer.record_stale_sweep_check(progress, hit) if not isinstance(result, StaleSweepProgress): raise TypeError("record_stale_sweep_check returned non-StaleSweepProgress result") @@ -202,7 +202,7 @@ def record_stale_sweep_check( def needs_inventory_fallback(*, bucket_counts_available: bool, coinset_scan_empty: bool) -> bool: return bool( - policy_kernel().needs_inventory_fallback( + policy_engine().needs_inventory_fallback( bool(bucket_counts_available), bool(coinset_scan_empty), ) @@ -216,7 +216,7 @@ def resolve_inventory_scan_source( wallet_scan_found_coins: bool, ) -> str: return str( - policy_kernel().resolve_inventory_scan_source( + policy_engine().resolve_inventory_scan_source( bool(coinset_scan_found_coins), bool(coinset_scan_empty), bool(cat_scan_found_coins), @@ -228,7 +228,7 @@ def resolve_inventory_scan_source( def resolve_tracked_sizes(ladder_sizes: list[int], strategy_default_sizes: list[int]) -> list[int]: return [ int(size) - for size in policy_kernel().resolve_tracked_sizes( + for size in policy_engine().resolve_tracked_sizes( [int(value) for value in ladder_sizes], [int(value) for value in strategy_default_sizes], ) @@ -236,7 +236,7 @@ def resolve_tracked_sizes(ladder_sizes: list[int], strategy_default_sizes: list[ def is_two_sided_market_mode(market_mode: str) -> bool: - return bool(policy_kernel().is_two_sided_market_mode(str(market_mode))) + return bool(policy_engine().is_two_sided_market_mode(str(market_mode))) def aggregate_two_sided_offer_counts( @@ -244,7 +244,7 @@ def aggregate_two_sided_offer_counts( sell_counts: dict[int, int], tracked_sizes: list[int], ) -> dict[int, int]: - signer = policy_kernel() + signer = policy_engine() result = signer.aggregate_two_sided_offer_counts( buy_counts, sell_counts, @@ -257,7 +257,7 @@ def one_sided_offer_counts_by_side( sell_counts: dict[int, int], tracked_sizes: list[int], ) -> dict[str, dict[int, int]]: - signer = policy_kernel() + signer = policy_engine() result = signer.one_sided_offer_counts_by_side( sell_counts, [int(size) for size in tracked_sizes] ) @@ -268,5 +268,5 @@ def one_sided_offer_counts_by_side( def executed_sell_offer_counts_by_size(action_items: list[Any]) -> dict[int, int]: - result = policy_kernel().executed_sell_offer_counts_by_size(action_items) + result = policy_engine().executed_sell_offer_counts_by_size(action_items) return require_i64_i64_map(result, label="executed_sell_offer_counts_by_size") diff --git a/greenfloor/core/cycle_reseed.py b/greenfloor/core/cycle_reseed.py index e1471033..ec4b12dc 100644 --- a/greenfloor/core/cycle_reseed.py +++ b/greenfloor/core/cycle_reseed.py @@ -9,7 +9,7 @@ class ReseedSkipReason(StrEnum): - """Member values must match `reseed_skip_reason_labels()` from the Rust reseed kernel.""" + """Member values must match `reseed_skip_reason_labels()` from the Rust reseed engine.""" STRATEGY_ACTIONS_PRESENT = "strategy_actions_present" ACTIVE_OFFER_TARGETS_SATISFIED = "active_offer_targets_satisfied" diff --git a/greenfloor/core/engine_bridge.py b/greenfloor/core/engine_bridge.py new file mode 100644 index 00000000..518a2346 --- /dev/null +++ b/greenfloor/core/engine_bridge.py @@ -0,0 +1,124 @@ +"""Shared PyO3 bridge for the Rust deterministic policy engine. + +The compiled extension is published as ``greenfloor_engine`` (ADR 0010). Python +callers should use :func:`import_engine`; ``import_signer`` remains as a migration alias. + +``policy_engine``, ``coin_ops_engine``, and ``bootstrap_engine`` are typed views of +the same PyO3 module — use the name that matches the ``Protocol`` at the call site. + +Deterministic policy bridges bind :func:`engine_method_getter` with the matching +typed view and ``missing`` label. Adapters and signing paths call ``import_engine()`` +directly. +""" + +from __future__ import annotations + +import importlib +from collections.abc import Callable +from typing import TYPE_CHECKING, Any, Literal, overload + +if TYPE_CHECKING: + from greenfloor.core.coin_ops.engine_protocol import CoinOpsEngineProtocol + from greenfloor.core.engine_protocol import BootstrapEngineProtocol, PolicyEngineProtocol + +ENGINE_MODULE = "greenfloor_engine" + +_MATURIN_INSTALL = ( + "`maturin develop --manifest-path greenfloor-engine-pyo3/Cargo.toml` from the repo root" +) + +__all__ = [ + "ENGINE_MODULE", + "bootstrap_engine", + "coin_ops_engine", + "import_engine", + "import_signer", + "engine_method_getter", + "engine_rebuild_hint", + "policy_engine", + "require_engine_method", +] + + +def engine_rebuild_hint(*, module: str, missing: str = "required engine") -> str: + """Operator-facing rebuild message for stale or incomplete PyO3 wheels.""" + return ( + f"{module} extension is missing {missing} symbols. " + f"Rebuild it (for example: {_MATURIN_INSTALL})." + ) + + +def import_engine() -> Any: + try: + return importlib.import_module(ENGINE_MODULE) + except ImportError as exc: + raise ImportError( + "Rust engine extension is not available " + f"(tried {ENGINE_MODULE}). " + f"Install via {_MATURIN_INSTALL}. {ENGINE_MODULE}: {exc}" + ) from exc + + +def _loaded_engine_module() -> Any: + return import_engine() + + +@overload +def typed_engine_view() -> PolicyEngineProtocol: ... + + +@overload +def typed_engine_view(*, kind: Literal["coin_ops"]) -> CoinOpsEngineProtocol: ... + + +@overload +def typed_engine_view(*, kind: Literal["bootstrap"]) -> BootstrapEngineProtocol: ... + + +def typed_engine_view(*, kind: str | None = None) -> Any: + del kind + return _loaded_engine_module() + + +def _engine_module_label(engine: Any) -> str: + name = getattr(engine, "__name__", None) + if isinstance(name, str) and name: + return name + return ENGINE_MODULE + + +def require_engine_method(engine: Any, method_name: str, *, missing: str) -> Any: + method = getattr(engine, method_name, None) + if method is None: + raise RuntimeError( + f"{engine_rebuild_hint(module=_engine_module_label(engine), missing=missing)} " + f"Missing symbol: {method_name}" + ) + return method + + +def engine_method_getter( + load_engine: Callable[[], Any], + *, + missing: str, +) -> Callable[[str], Any]: + def get_engine_method(method_name: str) -> Any: + return require_engine_method(load_engine(), method_name, missing=missing) + + return get_engine_method + + +def policy_engine() -> PolicyEngineProtocol: + return typed_engine_view() + + +def coin_ops_engine() -> CoinOpsEngineProtocol: + return typed_engine_view(kind="coin_ops") + + +def bootstrap_engine() -> BootstrapEngineProtocol: + return typed_engine_view(kind="bootstrap") + + +# Migration alias — prefer import_engine for new code. +import_signer = import_engine diff --git a/greenfloor/core/engine_maps.py b/greenfloor/core/engine_maps.py new file mode 100644 index 00000000..bcef1c9e --- /dev/null +++ b/greenfloor/core/engine_maps.py @@ -0,0 +1,22 @@ +"""Shared coercion helpers for engine dict return values.""" + +from __future__ import annotations + + +def require_i64_i64_map(value: object, *, label: str = "engine") -> dict[int, int]: + if not isinstance(value, dict): + raise TypeError(f"{label} returned non-dict result") + return {int(key): int(val) for key, val in value.items()} + + +def require_side_offer_count_maps( + value: object, + *, + label: str, +) -> dict[str, dict[int, int]]: + if not isinstance(value, dict): + raise TypeError(f"{label} returned non-dict result") + return { + "buy": require_i64_i64_map(value.get("buy", {}), label=f"{label}.buy"), + "sell": require_i64_i64_map(value.get("sell", {}), label=f"{label}.sell"), + } diff --git a/greenfloor/core/engine_protocol.py b/greenfloor/core/engine_protocol.py new file mode 100644 index 00000000..ba51e8dd --- /dev/null +++ b/greenfloor/core/engine_protocol.py @@ -0,0 +1,380 @@ +"""Composed PyO3 protocol surfaces for the deterministic policy engine.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Protocol + +from greenfloor.core.coin_ops.engine_protocol import CoinOpsEngineProtocol +from greenfloor.core.cycle_orchestration import ( + MarketBatchSelection, + OfferStateRow, + StaleSweepCandidate, + StaleSweepHit, + StaleSweepProgress, +) +from greenfloor.core.managed_action_outcome import ManagedActionOutcome +from greenfloor.core.managed_retry import ManagedRetryDecision +from greenfloor.core.parallel_batch_plan import ParallelBatchPlan +from greenfloor.core.parallel_reservation_context import ParallelReservationContext +from greenfloor.core.planned_action import PlannedAction +from greenfloor.core.strategy_action_item import StrategyActionItem + +if TYPE_CHECKING: + from greenfloor.core.cancel_policy import CancelPolicyDecision, OpenOfferRow + from greenfloor.core.notifications import LowInventoryEvaluation, LowInventoryInput + from greenfloor.offer_bootstrap import ( + BootstrapCoin, + BootstrapPhaseResult, + BootstrapPlanOutcome, + PlannerLadderRow, + ) + +__all__ = [ + "BootstrapEngineProtocol", + "CancelPolicyEngineProtocol", + "CoinOpsEngineProtocol", + "CycleEngineProtocol", + "DeterministicPolicyEngineProtocol", + "NotificationEngineProtocol", + "OfferPolicyEngineProtocol", + "PolicyEngineProtocol", + "RetryPolicyEngineProtocol", +] + + +class CycleEngineProtocol(Protocol): + def evaluate_market(self, state: Any, config: Any) -> list[PlannedAction]: ... + + def evaluate_two_sided_market_actions( + self, + buy_state: Any, + sell_state: Any, + buy_config: Any, + sell_config: Any, + ) -> list[PlannedAction]: ... + + def reseed_skip_reason_labels(self) -> tuple[str, ...]: ... + + def plan_reseed_actions_from_gap( + self, + strategy_actions: list[PlannedAction], + active_counts_by_size: dict[int, int], + target_counts_by_size: dict[int, int], + strategy_config: Any, + xch_price_usd: float | None, + ) -> Any: ... + + def sequential_action_route( + self, + runtime_dry_run: bool, + program_present: bool, + managed_backend_available: bool, + ) -> str: ... + + def expand_planned_actions(self, actions: list[PlannedAction]) -> list[PlannedAction]: ... + + def filter_planned_actions_with_positive_repeat( + self, + actions: list[PlannedAction], + ) -> list[PlannedAction]: ... + + def plan_parallel_managed_dispatch( + self, + actions: list[PlannedAction], + ctx: ParallelReservationContext, + spendable_profiles: dict[str, dict[str, int | bool]], + ) -> ParallelBatchPlan: ... + + def apply_offer_signal(self, state: str, signal: str) -> dict[str, Any]: ... + + def expiry_seconds_for_action(self, expiry_unit: str, expiry_value: int) -> int | None: ... + + def single_input_preferred_skip_reason( + self, + requested_amounts: dict[str, int], + spendable_profiles: dict[str, dict[str, int | bool]], + ) -> str | None: ... + + def is_transient_managed_upstream_error_text(self, error_text: str) -> bool: ... + + def classify_managed_transient_error( + self, exception_class: str, error_text: str + ) -> str | None: ... + + def is_managed_upstream_transient_error( + self, exception_class: str, error_text: str + ) -> bool: ... + + def is_managed_worker_transient_error(self, exception_class: str, error_text: str) -> bool: ... + + def is_parallel_dispatch_transient_error( + self, exception_class: str, error_text: str + ) -> bool: ... + + def is_transient_dexie_visibility_404_error(self, error: str) -> bool: ... + + def can_parallelize_managed_offers( + self, + signer_path_configured: bool, + parallelism_enabled: bool, + runtime_dry_run: bool, + has_coordinator: bool, + ) -> bool: ... + + def parallel_max_workers(self, submission_count: int, configured_max: int) -> int: ... + + def reservation_release_status(self, is_executed: bool) -> str: ... + + def should_apply_parallel_transient_cooldown( + self, + transient_failures: int, + total_parallel: int, + cooldown_seconds: int, + ) -> bool: ... + + def managed_retry_decision( + self, + attempt_index: int, + attempts_max: int, + backoff_ms: int, + is_upstream_transient: bool, + ) -> ManagedRetryDecision: ... + + def classify_managed_post_result( + self, + success: bool, + error_text: str, + offer_id: str, + publish_venue: str, + ) -> ManagedActionOutcome: ... + + def classify_dexie_visibility_outcome( + self, + visible: bool, + visibility_error: str, + ) -> ManagedActionOutcome: ... + + def count_parallel_transient_failures(self, items: list[StrategyActionItem]) -> int: ... + + def select_market_batch( + self, + enabled_market_ids: list[str], + slot_count: int, + cursor: int, + immediate_requeue_ids: list[str], + ) -> MarketBatchSelection: ... + + def enqueue_immediate_requeue( + self, + immediate_requeue_ids: list[str], + market_id: str, + ) -> list[str]: ... + + def should_use_market_slot_dispatch( + self, enabled_market_count: int, slot_count: int + ) -> bool: ... + + def dedupe_sorted_market_ids(self, market_ids: list[str]) -> list[str]: ... + + def should_log_disabled_market( + self, now_monotonic: float, next_log_deadline: float + ) -> bool: ... + + def next_disabled_market_log_deadline( + self, now_monotonic: float, interval_seconds: int + ) -> float: ... + + def should_try_cat_inventory_fallback( + self, coinset_scan_empty: bool, base_asset: str + ) -> bool: ... + + def collect_stale_sweep_candidates( + self, + rows: list[OfferStateRow], + enabled_market_ids: list[str], + per_market_limit: int, + ) -> list[StaleSweepCandidate]: ... + + def classify_dexie_stale_offer_status(self, status: int) -> str | None: ... + + def is_dexie_offer_missing_error_text(self, error_text: str) -> bool: ... + + def record_stale_sweep_check( + self, + progress: StaleSweepProgress, + hit: StaleSweepHit | None, + ) -> StaleSweepProgress: ... + + def needs_inventory_fallback( + self, bucket_counts_available: bool, coinset_scan_empty: bool + ) -> bool: ... + + def resolve_inventory_scan_source( + self, + coinset_scan_found_coins: bool, + coinset_scan_empty: bool, + cat_scan_found_coins: bool, + wallet_scan_found_coins: bool, + ) -> str: ... + + def resolve_tracked_sizes( + self, ladder_sizes: list[int], strategy_default_sizes: list[int] + ) -> list[int]: ... + + def is_two_sided_market_mode(self, market_mode: str) -> bool: ... + + def aggregate_two_sided_offer_counts( + self, + buy_counts: dict[int, int], + sell_counts: dict[int, int], + tracked_sizes: list[int], + ) -> dict[int, int]: ... + + def one_sided_offer_counts_by_side( + self, + sell_counts: dict[int, int], + tracked_sizes: list[int], + ) -> dict[str, dict[int, int]]: ... + + def executed_sell_offer_counts_by_size( + self, + action_items: list[Any], + ) -> dict[int, int]: ... + + +class CancelPolicyEngineProtocol(Protocol): + def abs_move_bps(self, current: float | None, previous: float | None) -> float | None: ... + + def cancel_move_threshold_bps( + self, market_threshold: int | None, env_threshold: int | None + ) -> int: ... + + def evaluate_cancel_policy_decision( + self, + quote_asset_type: str, + cancel_policy_stable_vs_unstable: bool, + current_xch_price_usd: float | None, + previous_xch_price_usd: float | None, + market_threshold: int | None, + env_threshold: int | None, + ) -> CancelPolicyDecision: ... + + def collect_open_offer_ids_for_cancel(self, offers: list[OpenOfferRow]) -> list[str]: ... + + +class NotificationEngineProtocol(Protocol): + def evaluate_low_inventory_alert(self, input: LowInventoryInput) -> LowInventoryEvaluation: ... + + +class BootstrapEngineProtocol(Protocol): + def plan_bootstrap_mixed_outputs( + self, + *, + ladder_entries: list[PlannerLadderRow], + spendable_coins: list[BootstrapCoin], + ) -> BootstrapPlanOutcome: ... + + def bootstrap_early_phase( + self, + *, + outcome: BootstrapPlanOutcome, + ) -> BootstrapPhaseResult | None: ... + + def bootstrap_executed_phase( + self, + *, + remaining: BootstrapPlanOutcome, + ) -> BootstrapPhaseResult: ... + + +class OfferPolicyEngineProtocol(Protocol): + def resolve_offer_expiry_for_pricing(self, pricing: dict[str, Any]) -> tuple[str, int]: ... + + def resolve_quote_price_for_pricing(self, pricing: dict[str, Any]) -> float: ... + + def mojo_multiplier_for_leg( + self, pricing: dict[str, Any], field: str, asset_id: str + ) -> int: ... + + def verify_offer_for_dexie(self, offer: str) -> str | None: ... + + def bootstrap_block_error( + self, + bootstrap_status: str, + bootstrap_reason: str, + bootstrap_ready: bool, + ) -> str | None: ... + + def expected_publish_asset_fields( + self, + side: str, + base_symbol: str, + quote_asset: str, + resolved_base_asset_id: str, + resolved_quote_asset_id: str, + ) -> dict[str, str]: ... + + def dexie_offer_asset_expectation_error( + self, + offered: object, + requested: object, + expected_offered_asset_id: str, + expected_offered_symbol: str, + expected_requested_asset_id: str, + expected_requested_symbol: str, + ) -> str | None: ... + + +class RetryPolicyEngineProtocol(Protocol): + def parse_rate_limit_retry_seconds(self, error_text: str) -> float | None: ... + + def moderate_retry_sleep_seconds( + self, current_sleep: float, rate_limit_wait: float | None + ) -> float: ... + + def moderate_retry_next_sleep(self, current_sleep: float) -> float: ... + + def dexie_invalid_offer_should_retry( + self, error: str, attempt: int, max_attempts: int + ) -> bool: ... + + def dexie_invalid_offer_retry_sleep(self, attempt: int, initial_sleep: float) -> float: ... + + def coinset_fee_lookup_retry_sleep(self, attempt: int) -> float: ... + + def poll_exponential_sleep_now( + self, + elapsed_seconds: int, + timeout_seconds: int, + sleep_seconds: float, + initial_sleep: float, + max_sleep: float, + ) -> float | None: ... + + def poll_exponential_advance_sleep( + self, + sleep_seconds: float, + initial_sleep: float, + max_sleep: float, + multiplier: float, + ) -> float: ... + + +class DeterministicPolicyEngineProtocol( + CycleEngineProtocol, + CancelPolicyEngineProtocol, + NotificationEngineProtocol, + Protocol, +): + """Cycle, cancel, and notification deterministic policy bindings.""" + + +class PolicyEngineProtocol( + DeterministicPolicyEngineProtocol, + CoinOpsEngineProtocol, + BootstrapEngineProtocol, + OfferPolicyEngineProtocol, + RetryPolicyEngineProtocol, + Protocol, +): + """Full in-process deterministic policy engine (cycle, coin-ops, offer, retry).""" diff --git a/greenfloor/core/kernel_bridge.py b/greenfloor/core/kernel_bridge.py index 3e5a4d62..558db69f 100644 --- a/greenfloor/core/kernel_bridge.py +++ b/greenfloor/core/kernel_bridge.py @@ -1,32 +1,30 @@ -"""Shared PyO3 bridge for the Rust deterministic policy kernel. +"""Compatibility shim for the renamed engine bridge. -The compiled extension is published as ``greenfloor_kernel`` (ADR 0010). Python -callers should use :func:`import_kernel`; ``import_signer`` remains as a migration alias. - -``policy_kernel``, ``coin_ops_kernel``, and ``bootstrap_kernel`` are typed views of -the same PyO3 module — use the name that matches the ``Protocol`` at the call site. - -Deterministic policy bridges bind :func:`kernel_method_getter` with the matching -typed view and ``missing`` label. Adapters and signing paths call ``import_kernel()`` -directly. +New code should import from :mod:`greenfloor.core.engine_bridge`. """ -from __future__ import annotations - -import importlib -from collections.abc import Callable -from typing import TYPE_CHECKING, Any, Literal, overload - -if TYPE_CHECKING: - from greenfloor.core.coin_ops.kernel_protocol import CoinOpsKernelProtocol - from greenfloor.core.kernel_protocol import BootstrapKernelProtocol, PolicyKernelProtocol - -KERNEL_MODULE = "greenfloor_kernel" - -_MATURIN_INSTALL = ( - "`maturin develop --manifest-path greenfloor-signer-pyo3/Cargo.toml` from the repo root" +from greenfloor.core.engine_bridge import ( + ENGINE_MODULE as KERNEL_MODULE, +) +from greenfloor.core.engine_bridge import ( + bootstrap_engine, + coin_ops_engine, + engine_method_getter, + engine_rebuild_hint, + import_engine, + import_signer, + policy_engine, + require_engine_method, ) +import_kernel = import_engine +policy_kernel = policy_engine +coin_ops_kernel = coin_ops_engine +bootstrap_kernel = bootstrap_engine +kernel_method_getter = engine_method_getter +kernel_rebuild_hint = engine_rebuild_hint +require_kernel_method = require_engine_method + __all__ = [ "KERNEL_MODULE", "bootstrap_kernel", @@ -38,87 +36,3 @@ "policy_kernel", "require_kernel_method", ] - - -def kernel_rebuild_hint(*, module: str, missing: str = "required kernel") -> str: - """Operator-facing rebuild message for stale or incomplete PyO3 wheels.""" - return ( - f"{module} extension is missing {missing} symbols. " - f"Rebuild it (for example: {_MATURIN_INSTALL})." - ) - - -def import_kernel() -> Any: - try: - return importlib.import_module(KERNEL_MODULE) - except ImportError as exc: - raise ImportError( - "Rust kernel extension is not available " - f"(tried {KERNEL_MODULE}). " - f"Install via {_MATURIN_INSTALL}. {KERNEL_MODULE}: {exc}" - ) from exc - - -def _loaded_kernel_module() -> Any: - return import_kernel() - - -@overload -def typed_kernel_view() -> PolicyKernelProtocol: ... - - -@overload -def typed_kernel_view(*, kind: Literal["coin_ops"]) -> CoinOpsKernelProtocol: ... - - -@overload -def typed_kernel_view(*, kind: Literal["bootstrap"]) -> BootstrapKernelProtocol: ... - - -def typed_kernel_view(*, kind: str | None = None) -> Any: - del kind - return _loaded_kernel_module() - - -def _kernel_module_label(kernel: Any) -> str: - name = getattr(kernel, "__name__", None) - if isinstance(name, str) and name: - return name - return KERNEL_MODULE - - -def require_kernel_method(kernel: Any, method_name: str, *, missing: str) -> Any: - method = getattr(kernel, method_name, None) - if method is None: - raise RuntimeError( - f"{kernel_rebuild_hint(module=_kernel_module_label(kernel), missing=missing)} " - f"Missing symbol: {method_name}" - ) - return method - - -def kernel_method_getter( - load_kernel: Callable[[], Any], - *, - missing: str, -) -> Callable[[str], Any]: - def get_kernel_method(method_name: str) -> Any: - return require_kernel_method(load_kernel(), method_name, missing=missing) - - return get_kernel_method - - -def policy_kernel() -> PolicyKernelProtocol: - return typed_kernel_view() - - -def coin_ops_kernel() -> CoinOpsKernelProtocol: - return typed_kernel_view(kind="coin_ops") - - -def bootstrap_kernel() -> BootstrapKernelProtocol: - return typed_kernel_view(kind="bootstrap") - - -# Migration alias — prefer import_kernel for new code. -import_signer = import_kernel diff --git a/greenfloor/core/kernel_maps.py b/greenfloor/core/kernel_maps.py index 05224810..65f9b4e7 100644 --- a/greenfloor/core/kernel_maps.py +++ b/greenfloor/core/kernel_maps.py @@ -1,22 +1,5 @@ -"""Shared coercion helpers for kernel dict return values.""" +"""Compatibility shim for engine map coercion helpers.""" -from __future__ import annotations +from greenfloor.core.engine_maps import require_i64_i64_map, require_side_offer_count_maps - -def require_i64_i64_map(value: object, *, label: str = "kernel") -> dict[int, int]: - if not isinstance(value, dict): - raise TypeError(f"{label} returned non-dict result") - return {int(key): int(val) for key, val in value.items()} - - -def require_side_offer_count_maps( - value: object, - *, - label: str, -) -> dict[str, dict[int, int]]: - if not isinstance(value, dict): - raise TypeError(f"{label} returned non-dict result") - return { - "buy": require_i64_i64_map(value.get("buy", {}), label=f"{label}.buy"), - "sell": require_i64_i64_map(value.get("sell", {}), label=f"{label}.sell"), - } +__all__ = ["require_i64_i64_map", "require_side_offer_count_maps"] diff --git a/greenfloor/core/kernel_protocol.py b/greenfloor/core/kernel_protocol.py index 4744506d..a285082e 100644 --- a/greenfloor/core/kernel_protocol.py +++ b/greenfloor/core/kernel_protocol.py @@ -1,33 +1,29 @@ -"""Composed PyO3 protocol surfaces for the deterministic policy kernel.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Protocol - -from greenfloor.core.coin_ops.kernel_protocol import CoinOpsKernelProtocol -from greenfloor.core.cycle_orchestration import ( - MarketBatchSelection, - OfferStateRow, - StaleSweepCandidate, - StaleSweepHit, - StaleSweepProgress, +"""Compatibility shim for engine protocol names. + +New code should import from :mod:`greenfloor.core.engine_protocol`. +""" + +from greenfloor.core.engine_protocol import ( + BootstrapEngineProtocol, + CancelPolicyEngineProtocol, + CoinOpsEngineProtocol, + CycleEngineProtocol, + DeterministicPolicyEngineProtocol, + NotificationEngineProtocol, + OfferPolicyEngineProtocol, + PolicyEngineProtocol, + RetryPolicyEngineProtocol, ) -from greenfloor.core.managed_action_outcome import ManagedActionOutcome -from greenfloor.core.managed_retry import ManagedRetryDecision -from greenfloor.core.parallel_batch_plan import ParallelBatchPlan -from greenfloor.core.parallel_reservation_context import ParallelReservationContext -from greenfloor.core.planned_action import PlannedAction -from greenfloor.core.strategy_action_item import StrategyActionItem -if TYPE_CHECKING: - from greenfloor.core.cancel_policy import CancelPolicyDecision, OpenOfferRow - from greenfloor.core.notifications import LowInventoryEvaluation, LowInventoryInput - from greenfloor.offer_bootstrap import ( - BootstrapCoin, - BootstrapPhaseResult, - BootstrapPlanOutcome, - PlannerLadderRow, - ) +BootstrapKernelProtocol = BootstrapEngineProtocol +CancelPolicyKernelProtocol = CancelPolicyEngineProtocol +CoinOpsKernelProtocol = CoinOpsEngineProtocol +CycleKernelProtocol = CycleEngineProtocol +DeterministicPolicyKernelProtocol = DeterministicPolicyEngineProtocol +NotificationKernelProtocol = NotificationEngineProtocol +OfferPolicyKernelProtocol = OfferPolicyEngineProtocol +PolicyKernelProtocol = PolicyEngineProtocol +RetryPolicyKernelProtocol = RetryPolicyEngineProtocol __all__ = [ "BootstrapKernelProtocol", @@ -40,341 +36,3 @@ "PolicyKernelProtocol", "RetryPolicyKernelProtocol", ] - - -class CycleKernelProtocol(Protocol): - def evaluate_market(self, state: Any, config: Any) -> list[PlannedAction]: ... - - def evaluate_two_sided_market_actions( - self, - buy_state: Any, - sell_state: Any, - buy_config: Any, - sell_config: Any, - ) -> list[PlannedAction]: ... - - def reseed_skip_reason_labels(self) -> tuple[str, ...]: ... - - def plan_reseed_actions_from_gap( - self, - strategy_actions: list[PlannedAction], - active_counts_by_size: dict[int, int], - target_counts_by_size: dict[int, int], - strategy_config: Any, - xch_price_usd: float | None, - ) -> Any: ... - - def sequential_action_route( - self, - runtime_dry_run: bool, - program_present: bool, - managed_backend_available: bool, - ) -> str: ... - - def expand_planned_actions(self, actions: list[PlannedAction]) -> list[PlannedAction]: ... - - def filter_planned_actions_with_positive_repeat( - self, - actions: list[PlannedAction], - ) -> list[PlannedAction]: ... - - def plan_parallel_managed_dispatch( - self, - actions: list[PlannedAction], - ctx: ParallelReservationContext, - spendable_profiles: dict[str, dict[str, int | bool]], - ) -> ParallelBatchPlan: ... - - def apply_offer_signal(self, state: str, signal: str) -> dict[str, Any]: ... - - def expiry_seconds_for_action(self, expiry_unit: str, expiry_value: int) -> int | None: ... - - def single_input_preferred_skip_reason( - self, - requested_amounts: dict[str, int], - spendable_profiles: dict[str, dict[str, int | bool]], - ) -> str | None: ... - - def is_transient_managed_upstream_error_text(self, error_text: str) -> bool: ... - - def classify_managed_transient_error( - self, exception_class: str, error_text: str - ) -> str | None: ... - - def is_managed_upstream_transient_error( - self, exception_class: str, error_text: str - ) -> bool: ... - - def is_managed_worker_transient_error(self, exception_class: str, error_text: str) -> bool: ... - - def is_parallel_dispatch_transient_error( - self, exception_class: str, error_text: str - ) -> bool: ... - - def is_transient_dexie_visibility_404_error(self, error: str) -> bool: ... - - def can_parallelize_managed_offers( - self, - signer_path_configured: bool, - parallelism_enabled: bool, - runtime_dry_run: bool, - has_coordinator: bool, - ) -> bool: ... - - def parallel_max_workers(self, submission_count: int, configured_max: int) -> int: ... - - def reservation_release_status(self, is_executed: bool) -> str: ... - - def should_apply_parallel_transient_cooldown( - self, - transient_failures: int, - total_parallel: int, - cooldown_seconds: int, - ) -> bool: ... - - def managed_retry_decision( - self, - attempt_index: int, - attempts_max: int, - backoff_ms: int, - is_upstream_transient: bool, - ) -> ManagedRetryDecision: ... - - def classify_managed_post_result( - self, - success: bool, - error_text: str, - offer_id: str, - publish_venue: str, - ) -> ManagedActionOutcome: ... - - def classify_dexie_visibility_outcome( - self, - visible: bool, - visibility_error: str, - ) -> ManagedActionOutcome: ... - - def count_parallel_transient_failures(self, items: list[StrategyActionItem]) -> int: ... - - def select_market_batch( - self, - enabled_market_ids: list[str], - slot_count: int, - cursor: int, - immediate_requeue_ids: list[str], - ) -> MarketBatchSelection: ... - - def enqueue_immediate_requeue( - self, - immediate_requeue_ids: list[str], - market_id: str, - ) -> list[str]: ... - - def should_use_market_slot_dispatch( - self, enabled_market_count: int, slot_count: int - ) -> bool: ... - - def dedupe_sorted_market_ids(self, market_ids: list[str]) -> list[str]: ... - - def should_log_disabled_market( - self, now_monotonic: float, next_log_deadline: float - ) -> bool: ... - - def next_disabled_market_log_deadline( - self, now_monotonic: float, interval_seconds: int - ) -> float: ... - - def should_try_cat_inventory_fallback( - self, coinset_scan_empty: bool, base_asset: str - ) -> bool: ... - - def collect_stale_sweep_candidates( - self, - rows: list[OfferStateRow], - enabled_market_ids: list[str], - per_market_limit: int, - ) -> list[StaleSweepCandidate]: ... - - def classify_dexie_stale_offer_status(self, status: int) -> str | None: ... - - def is_dexie_offer_missing_error_text(self, error_text: str) -> bool: ... - - def record_stale_sweep_check( - self, - progress: StaleSweepProgress, - hit: StaleSweepHit | None, - ) -> StaleSweepProgress: ... - - def needs_inventory_fallback( - self, bucket_counts_available: bool, coinset_scan_empty: bool - ) -> bool: ... - - def resolve_inventory_scan_source( - self, - coinset_scan_found_coins: bool, - coinset_scan_empty: bool, - cat_scan_found_coins: bool, - wallet_scan_found_coins: bool, - ) -> str: ... - - def resolve_tracked_sizes( - self, ladder_sizes: list[int], strategy_default_sizes: list[int] - ) -> list[int]: ... - - def is_two_sided_market_mode(self, market_mode: str) -> bool: ... - - def aggregate_two_sided_offer_counts( - self, - buy_counts: dict[int, int], - sell_counts: dict[int, int], - tracked_sizes: list[int], - ) -> dict[int, int]: ... - - def one_sided_offer_counts_by_side( - self, - sell_counts: dict[int, int], - tracked_sizes: list[int], - ) -> dict[str, dict[int, int]]: ... - - def executed_sell_offer_counts_by_size( - self, - action_items: list[Any], - ) -> dict[int, int]: ... - - -class CancelPolicyKernelProtocol(Protocol): - def abs_move_bps(self, current: float | None, previous: float | None) -> float | None: ... - - def cancel_move_threshold_bps( - self, market_threshold: int | None, env_threshold: int | None - ) -> int: ... - - def evaluate_cancel_policy_decision( - self, - quote_asset_type: str, - cancel_policy_stable_vs_unstable: bool, - current_xch_price_usd: float | None, - previous_xch_price_usd: float | None, - market_threshold: int | None, - env_threshold: int | None, - ) -> CancelPolicyDecision: ... - - def collect_open_offer_ids_for_cancel(self, offers: list[OpenOfferRow]) -> list[str]: ... - - -class NotificationKernelProtocol(Protocol): - def evaluate_low_inventory_alert(self, input: LowInventoryInput) -> LowInventoryEvaluation: ... - - -class BootstrapKernelProtocol(Protocol): - def plan_bootstrap_mixed_outputs( - self, - *, - ladder_entries: list[PlannerLadderRow], - spendable_coins: list[BootstrapCoin], - ) -> BootstrapPlanOutcome: ... - - def bootstrap_early_phase( - self, - *, - outcome: BootstrapPlanOutcome, - ) -> BootstrapPhaseResult | None: ... - - def bootstrap_executed_phase( - self, - *, - remaining: BootstrapPlanOutcome, - ) -> BootstrapPhaseResult: ... - - -class OfferPolicyKernelProtocol(Protocol): - def resolve_offer_expiry_for_pricing(self, pricing: dict[str, Any]) -> tuple[str, int]: ... - - def resolve_quote_price_for_pricing(self, pricing: dict[str, Any]) -> float: ... - - def mojo_multiplier_for_leg( - self, pricing: dict[str, Any], field: str, asset_id: str - ) -> int: ... - - def verify_offer_for_dexie(self, offer: str) -> str | None: ... - - def bootstrap_block_error( - self, - bootstrap_status: str, - bootstrap_reason: str, - bootstrap_ready: bool, - ) -> str | None: ... - - def expected_publish_asset_fields( - self, - side: str, - base_symbol: str, - quote_asset: str, - resolved_base_asset_id: str, - resolved_quote_asset_id: str, - ) -> dict[str, str]: ... - - def dexie_offer_asset_expectation_error( - self, - offered: object, - requested: object, - expected_offered_asset_id: str, - expected_offered_symbol: str, - expected_requested_asset_id: str, - expected_requested_symbol: str, - ) -> str | None: ... - - -class RetryPolicyKernelProtocol(Protocol): - def parse_rate_limit_retry_seconds(self, error_text: str) -> float | None: ... - - def moderate_retry_sleep_seconds( - self, current_sleep: float, rate_limit_wait: float | None - ) -> float: ... - - def moderate_retry_next_sleep(self, current_sleep: float) -> float: ... - - def dexie_invalid_offer_should_retry( - self, error: str, attempt: int, max_attempts: int - ) -> bool: ... - - def dexie_invalid_offer_retry_sleep(self, attempt: int, initial_sleep: float) -> float: ... - - def coinset_fee_lookup_retry_sleep(self, attempt: int) -> float: ... - - def poll_exponential_sleep_now( - self, - elapsed_seconds: int, - timeout_seconds: int, - sleep_seconds: float, - initial_sleep: float, - max_sleep: float, - ) -> float | None: ... - - def poll_exponential_advance_sleep( - self, - sleep_seconds: float, - initial_sleep: float, - max_sleep: float, - multiplier: float, - ) -> float: ... - - -class DeterministicPolicyKernelProtocol( - CycleKernelProtocol, - CancelPolicyKernelProtocol, - NotificationKernelProtocol, - Protocol, -): - """Cycle, cancel, and notification deterministic policy bindings.""" - - -class PolicyKernelProtocol( - DeterministicPolicyKernelProtocol, - CoinOpsKernelProtocol, - BootstrapKernelProtocol, - OfferPolicyKernelProtocol, - RetryPolicyKernelProtocol, - Protocol, -): - """Full in-process deterministic policy kernel (cycle, coin-ops, offer, retry).""" diff --git a/greenfloor/core/notifications.py b/greenfloor/core/notifications.py index 695ae198..625c193a 100644 --- a/greenfloor/core/notifications.py +++ b/greenfloor/core/notifications.py @@ -4,7 +4,7 @@ from datetime import UTC, datetime from greenfloor.config.models import MarketConfig, ProgramConfig -from greenfloor.core.kernel_bridge import policy_kernel +from greenfloor.core.engine_bridge import policy_engine @dataclass(frozen=True, slots=True) @@ -102,7 +102,7 @@ def evaluate_low_inventory_alert( market: MarketConfig, state: AlertState, ) -> tuple[AlertState, AlertEvent | None]: - evaluation = policy_kernel().evaluate_low_inventory_alert( + evaluation = policy_engine().evaluate_low_inventory_alert( _low_inventory_input(now=now, program=program, market=market, state=state) ) if not isinstance(evaluation, LowInventoryEvaluation): diff --git a/greenfloor/core/offer_action.py b/greenfloor/core/offer_action.py index dbb17424..046681b0 100644 --- a/greenfloor/core/offer_action.py +++ b/greenfloor/core/offer_action.py @@ -68,7 +68,7 @@ def build_action_request( broadcast_split: bool = True, offer_coin_ids: list[str] | None = None, ) -> OfferActionRequest: - """Shape a kernel ``BuildOfferForActionRequest`` dict.""" + """Shape a engine ``BuildOfferForActionRequest`` dict.""" address = str(receive_address or "").strip() if not address: raise ValueError("market.receive_address is required for offer build") @@ -146,7 +146,7 @@ def legacy_action_request_from_payload(payload: dict[str, Any]) -> OfferActionRe def parse_action_result(payload: object) -> OfferActionResult: if not isinstance(payload, dict): - raise TypeError("offer action kernel returned non-dict result") + raise TypeError("offer action engine returned non-dict result") offer_text = str(payload.get("offer_text", "")).strip() if not offer_text.startswith("offer1"): raise RuntimeError("offer_action_failed:missing_offer_text") @@ -183,7 +183,7 @@ def to_create_phase_outcome( *, action_side: str, ) -> OfferCreatePhaseOutcome: - """Map kernel action result to signer/local create-phase fields.""" + """Map engine action result to signer/local create-phase fields.""" return OfferCreatePhaseOutcome( offer_text=result["offer_text"], expires_at=expires_at_iso_from_unix(result["expires_at_unix"]), diff --git a/greenfloor/core/offer_bootstrap_bridge.py b/greenfloor/core/offer_bootstrap_bridge.py index 47c0bfbb..78aeb323 100644 --- a/greenfloor/core/offer_bootstrap_bridge.py +++ b/greenfloor/core/offer_bootstrap_bridge.py @@ -1,11 +1,11 @@ """Stable runtime import path for bootstrap planner, phase policy, and DTOs. -Kernel-backed symbols live here. Call via this module (not ``kernel_bridge.bootstrap_kernel()``). +Engine-backed symbols live here. Call via this module (not ``engine_bridge.bootstrap_engine()``). Coinset coin dicts are coerced to ``BootstrapCoin`` at the planner boundary; PyO3 requires ``BootstrapCoin`` instances. **Policy ownership:** deterministic planner + early/executed phase mapping are Rust -(``greenfloor-signer/src/offer/bootstrap/``). **Fee eligibility** and mixed-split I/O are +(``greenfloor-engine/src/offer/bootstrap/``). **Fee eligibility** and mixed-split I/O are Python-only (``greenfloor/runtime/offer_bootstrap.py``). """ @@ -13,7 +13,7 @@ from typing import Any -from greenfloor.core import kernel_bridge +from greenfloor.core import engine_bridge from greenfloor.offer_bootstrap import ( BootstrapCoin, BootstrapPhaseResult, @@ -36,8 +36,8 @@ ] -_require_bootstrap_method = kernel_bridge.kernel_method_getter( - lambda: kernel_bridge.bootstrap_kernel(), +_require_bootstrap_method = engine_bridge.engine_method_getter( + lambda: engine_bridge.bootstrap_engine(), missing="bootstrap planner", ) @@ -75,7 +75,7 @@ def _coerce_planner_outcome(payload: object) -> BootstrapPlanOutcome: def _coerce_phase_result(payload: object) -> BootstrapPhaseResult: if isinstance(payload, BootstrapPhaseResult): return payload - raise TypeError("bootstrap phase kernel call returned unexpected result type") + raise TypeError("bootstrap phase engine call returned unexpected result type") def plan_bootstrap_mixed_outputs( diff --git a/greenfloor/core/offer_reconcile/__init__.py b/greenfloor/core/offer_reconcile/__init__.py index 3b027c3e..4a1f9aa7 100644 --- a/greenfloor/core/offer_reconcile/__init__.py +++ b/greenfloor/core/offer_reconcile/__init__.py @@ -1,4 +1,4 @@ -"""Offer reconciliation transition kernel (Rust-backed).""" +"""Offer reconciliation transition engine (Rust-backed).""" from __future__ import annotations @@ -6,7 +6,7 @@ from dataclasses import dataclass from typing import TypeVar -from greenfloor.core.kernel_bridge import import_kernel +from greenfloor.core.engine_bridge import import_engine __all__ = [ "CycleOfferTransition", @@ -47,7 +47,7 @@ def _typed_transition(call: _CallableT, /, *args: object, **kwargs: object) -> C def resolve_missing_watched_offer_transition(*, current_state: str) -> CycleOfferTransition: return _typed_transition( - import_kernel().resolve_missing_watched_offer_transition, + import_engine().resolve_missing_watched_offer_transition, str(current_state), ) @@ -61,7 +61,7 @@ def resolve_watched_offer_transition_from_signals( coinset_mempool_tx_ids: list[str], ) -> CycleOfferTransition: return _typed_transition( - import_kernel().resolve_watched_offer_transition_from_signals, + import_engine().resolve_watched_offer_transition_from_signals, str(current_state), status, list(coinset_tx_ids), @@ -72,7 +72,7 @@ def resolve_watched_offer_transition_from_signals( def unchanged_offer_transition(*, current_state: str, reason: str) -> CycleOfferTransition: return _typed_transition( - import_kernel().unchanged_offer_transition, + import_engine().unchanged_offer_transition, str(current_state), str(reason), ) @@ -80,7 +80,7 @@ def unchanged_offer_transition(*, current_state: str, reason: str) -> CycleOffer def unsupported_venue_offer_transition(*, current_state: str, venue: str) -> CycleOfferTransition: return _typed_transition( - import_kernel().unsupported_venue_offer_transition, + import_engine().unsupported_venue_offer_transition, str(current_state), str(venue), ) diff --git a/greenfloor/core/offer_request_bridge.py b/greenfloor/core/offer_request_bridge.py index 02c17bf2..fabe7676 100644 --- a/greenfloor/core/offer_request_bridge.py +++ b/greenfloor/core/offer_request_bridge.py @@ -7,10 +7,10 @@ if TYPE_CHECKING: from greenfloor.core.signer_offer_request import SignerOfferLegAmounts -from greenfloor.core import kernel_bridge +from greenfloor.core import engine_bridge -_require_offer_request_method = kernel_bridge.kernel_method_getter( - lambda: kernel_bridge.policy_kernel(), +_require_offer_request_method = engine_bridge.engine_method_getter( + lambda: engine_bridge.policy_engine(), missing="offer-request", ) @@ -24,7 +24,7 @@ def _coerce_signer_offer_leg_amounts(payload: object): def normalize_offer_side(action_side: str) -> str: - """Normalize to ``buy`` or ``sell``. Fast path for common inputs; kernel for the rest.""" + """Normalize to ``buy`` or ``sell``. Fast path for common inputs; engine for the rest.""" trimmed = str(action_side or "").strip() if not trimmed: return "sell" diff --git a/greenfloor/core/planned_action.py b/greenfloor/core/planned_action.py index 4420adb5..8062967b 100644 --- a/greenfloor/core/planned_action.py +++ b/greenfloor/core/planned_action.py @@ -4,7 +4,7 @@ from dataclasses import dataclass -# Cycle kernel and strategy evaluation emit only these labels. +# Cycle engine and strategy evaluation emit only these labels. _PLANNED_OFFER_SIDES = frozenset({"buy", "sell"}) @@ -22,7 +22,7 @@ class PlannedAction: def planned_action_side(action: PlannedAction) -> str: - """Return ``buy`` or ``sell`` without a kernel round-trip when side is already canonical.""" + """Return ``buy`` or ``sell`` without a engine round-trip when side is already canonical.""" side = str(action.side or "sell").strip().lower() if side in _PLANNED_OFFER_SIDES: return side diff --git a/greenfloor/core/policy_bridge.py b/greenfloor/core/policy_bridge.py index 230964c2..98718f21 100644 --- a/greenfloor/core/policy_bridge.py +++ b/greenfloor/core/policy_bridge.py @@ -4,7 +4,7 @@ from typing import Any, TypedDict -from greenfloor.core import kernel_bridge +from greenfloor.core import engine_bridge from greenfloor.core.offer_request_bridge import ( compute_signer_offer_leg_amounts, normalize_offer_asset_id, @@ -38,8 +38,8 @@ ] -_require_policy_method = kernel_bridge.kernel_method_getter( - lambda: kernel_bridge.policy_kernel(), +_require_policy_method = engine_bridge.engine_method_getter( + lambda: engine_bridge.policy_engine(), missing="required policy", ) @@ -72,16 +72,16 @@ def _coerce_expected_publish_asset_fields(payload: object) -> ExpectedPublishAss def resolve_offer_expiry_for_pricing(pricing: dict[str, Any]) -> tuple[str, int]: - unit, value = kernel_bridge.policy_kernel().resolve_offer_expiry_for_pricing(pricing) + unit, value = engine_bridge.policy_engine().resolve_offer_expiry_for_pricing(pricing) return str(unit), int(value) def resolve_quote_price_for_pricing(pricing: dict[str, Any]) -> float: - return float(kernel_bridge.policy_kernel().resolve_quote_price_for_pricing(pricing)) + return float(engine_bridge.policy_engine().resolve_quote_price_for_pricing(pricing)) def mojo_multiplier_for_leg(pricing: dict[str, Any], field: str, asset_id: str) -> int: - return int(kernel_bridge.policy_kernel().mojo_multiplier_for_leg(pricing, field, asset_id)) + return int(engine_bridge.policy_engine().mojo_multiplier_for_leg(pricing, field, asset_id)) def verify_offer_for_dexie(offer_text: str) -> str | None: @@ -89,7 +89,7 @@ def verify_offer_for_dexie(offer_text: str) -> str | None: verify_offer = _require_policy_method("verify_offer_for_dexie") error = verify_offer(offer_text) except ImportError: - return "wallet_sdk_import_error:greenfloor_kernel_unavailable" + return "wallet_sdk_import_error:greenfloor_engine_unavailable" return None if error is None else str(error) @@ -149,7 +149,7 @@ def dexie_offer_asset_expectation_error( def parse_rate_limit_retry_seconds(error_text: str) -> float | None: - value = kernel_bridge.policy_kernel().parse_rate_limit_retry_seconds(error_text) + value = engine_bridge.policy_engine().parse_rate_limit_retry_seconds(error_text) return None if value is None else float(value) @@ -159,19 +159,19 @@ def moderate_retry_sleep_seconds( rate_limit_wait: float | None, ) -> float: return float( - kernel_bridge.policy_kernel().moderate_retry_sleep_seconds( + engine_bridge.policy_engine().moderate_retry_sleep_seconds( float(current_sleep), rate_limit_wait ) ) def moderate_retry_next_sleep(current_sleep: float) -> float: - return float(kernel_bridge.policy_kernel().moderate_retry_next_sleep(float(current_sleep))) + return float(engine_bridge.policy_engine().moderate_retry_next_sleep(float(current_sleep))) def dexie_invalid_offer_should_retry(*, error: str, attempt: int, max_attempts: int) -> bool: return bool( - kernel_bridge.policy_kernel().dexie_invalid_offer_should_retry( + engine_bridge.policy_engine().dexie_invalid_offer_should_retry( str(error), int(attempt), int(max_attempts), @@ -181,14 +181,14 @@ def dexie_invalid_offer_should_retry(*, error: str, attempt: int, max_attempts: def dexie_invalid_offer_retry_sleep(*, attempt: int, initial_sleep: float) -> float: return float( - kernel_bridge.policy_kernel().dexie_invalid_offer_retry_sleep( + engine_bridge.policy_engine().dexie_invalid_offer_retry_sleep( int(attempt), float(initial_sleep) ) ) def coinset_fee_lookup_retry_sleep(attempt: int) -> float: - return float(kernel_bridge.policy_kernel().coinset_fee_lookup_retry_sleep(int(attempt))) + return float(engine_bridge.policy_engine().coinset_fee_lookup_retry_sleep(int(attempt))) def poll_exponential_sleep_now( @@ -199,7 +199,7 @@ def poll_exponential_sleep_now( initial_sleep: float, max_sleep: float, ) -> float | None: - value = kernel_bridge.policy_kernel().poll_exponential_sleep_now( + value = engine_bridge.policy_engine().poll_exponential_sleep_now( int(elapsed_seconds), int(timeout_seconds), float(sleep_seconds), @@ -217,7 +217,7 @@ def poll_exponential_advance_sleep( multiplier: float, ) -> float: return float( - kernel_bridge.policy_kernel().poll_exponential_advance_sleep( + engine_bridge.policy_engine().poll_exponential_advance_sleep( float(sleep_seconds), float(initial_sleep), float(max_sleep), diff --git a/greenfloor/core/signer_offer_request.py b/greenfloor/core/signer_offer_request.py index dd3b717b..c677524b 100644 --- a/greenfloor/core/signer_offer_request.py +++ b/greenfloor/core/signer_offer_request.py @@ -1,6 +1,6 @@ """Signer create-offer request types and builders (no IO). -Leg math and asset normalization live in the Rust kernel; Python reaches them via +Leg math and asset normalization live in the Rust engine; Python reaches them via ``greenfloor.core.offer_request_bridge``. This module owns dataclasses and request assembly. """ diff --git a/greenfloor/core/strategy_action_item.py b/greenfloor/core/strategy_action_item.py index 20c4281f..07a6e4b8 100644 --- a/greenfloor/core/strategy_action_item.py +++ b/greenfloor/core/strategy_action_item.py @@ -1,4 +1,4 @@ -"""Typed strategy offer action outcome (shared by cycle kernel and daemon dispatch).""" +"""Typed strategy offer action outcome (shared by cycle engine and daemon dispatch).""" from __future__ import annotations diff --git a/greenfloor/offer_builder.py b/greenfloor/offer_builder.py index 33ae0f0b..7229b4e9 100644 --- a/greenfloor/offer_builder.py +++ b/greenfloor/offer_builder.py @@ -1,4 +1,4 @@ -"""Legacy offer-builder entry point; ``build_offer`` uses the Rust kernel BLS action path.""" +"""Legacy offer-builder entry point; ``build_offer`` uses the Rust engine BLS action path.""" from __future__ import annotations diff --git a/greenfloor/runtime/offer_action_build.py b/greenfloor/runtime/offer_action_build.py index 248f2b83..26ba29b2 100644 --- a/greenfloor/runtime/offer_action_build.py +++ b/greenfloor/runtime/offer_action_build.py @@ -78,7 +78,7 @@ def build_bls_offer_from_build_context( quote_price: float | None = None, offer_coin_ids: list[str] | None = None, ) -> OfferActionResult: - """Build an offer via the Rust kernel BLS path.""" + """Build an offer via the Rust engine BLS path.""" market = build_ctx.market key_id = str(market.signer_key_id or "").strip() if not key_id: diff --git a/pyproject.toml b/pyproject.toml index bc1f7a60..8078134d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,5 +46,5 @@ ignore = ["E501"] testpaths = ["tests"] addopts = "--basetemp=.pytest_tmp" markers = [ - "signer: requires compiled greenfloor_kernel (validate_offer_structure)", + "engine: requires compiled greenfloor_engine (validate_offer_structure)", ] diff --git a/scripts/export_signer_fixtures.py b/scripts/export_signer_fixtures.py index 6f6f8818..5658842d 100644 --- a/scripts/export_signer_fixtures.py +++ b/scripts/export_signer_fixtures.py @@ -13,8 +13,8 @@ EXPORT_RS = """ use std::path::Path; use serde::Serialize; -use greenfloor_signer::offer::CreateOfferResult; -use greenfloor_signer::test_support::simulator::offer_roundtrips::{ +use greenfloor_engine::offer::CreateOfferResult; +use greenfloor_engine::test_support::simulator::offer_roundtrips::{ export_offer_fixture, OfferRoundtripScenario, }; @@ -60,7 +60,7 @@ def main() -> int: FIXTURE_DIR.mkdir(parents=True, exist_ok=True) - src_dir = REPO / "greenfloor-signer" / "tools" + src_dir = REPO / "greenfloor-engine" / "tools" src_dir.mkdir(exist_ok=True) src_path = src_dir / "export_fixtures.rs" src_path.write_text(EXPORT_RS.strip() + "\n", encoding="utf-8") @@ -70,7 +70,7 @@ def main() -> int: async fn export_signer_fixtures_to_disk() { use std::path::Path; use serde::Serialize; - use greenfloor_signer::test_support::simulator::offer_roundtrips::{ + use greenfloor_engine::test_support::simulator::offer_roundtrips::{ export_offer_fixture, OfferRoundtripScenario, }; #[derive(Serialize)] @@ -107,8 +107,8 @@ def main() -> int: } } """ - test_path = REPO / "greenfloor-signer" / "src" / "test_support" / "export_fixtures_test.rs" - mod_path = REPO / "greenfloor-signer" / "src" / "test_support" / "mod.rs" + test_path = REPO / "greenfloor-engine" / "src" / "test_support" / "export_fixtures_test.rs" + mod_path = REPO / "greenfloor-engine" / "src" / "test_support" / "mod.rs" if "export_fixtures_test" not in mod_path.read_text(encoding="utf-8"): mod_path.write_text( mod_path.read_text(encoding="utf-8") + "\npub mod export_fixtures_test;\n", @@ -117,7 +117,7 @@ def main() -> int: test_path.write_text(f"#[cfg(test)]\nmod tests {{\n{code}\n}}\n", encoding="utf-8") subprocess.run( ["cargo", "test", "export_signer_fixtures_to_disk", "--", "--nocapture"], - cwd=REPO / "greenfloor-signer", + cwd=REPO / "greenfloor-engine", check=True, ) print(f"exported fixtures to {FIXTURE_DIR}") diff --git a/tests/helpers/kernel_mock.py b/tests/helpers/engine_mock.py similarity index 79% rename from tests/helpers/kernel_mock.py rename to tests/helpers/engine_mock.py index 1076423e..8c26807c 100644 --- a/tests/helpers/kernel_mock.py +++ b/tests/helpers/engine_mock.py @@ -1,17 +1,17 @@ -"""Minimal greenfloor kernel stub helpers for partial kernel mocks in tests.""" +"""Minimal greenfloor engine stub helpers for partial engine mocks in tests.""" from __future__ import annotations from typing import Any -def install_kernel_stub(monkeypatch: Any, stub: Any) -> None: - """Register a stub for the ADR 0010 kernel module name.""" - monkeypatch.setitem(__import__("sys").modules, "greenfloor_kernel", stub) +def install_engine_stub(monkeypatch: Any, stub: Any) -> None: + """Register a stub for the ADR 0010 engine module name.""" + monkeypatch.setitem(__import__("sys").modules, "greenfloor_engine", stub) -def mock_kernel_normalize_hex_id(value: str) -> str: - """Mirror ``hex_utils.normalize_hex_id`` for tests that stub ``greenfloor_kernel``.""" +def mock_engine_normalize_hex_id(value: str) -> str: + """Mirror ``hex_utils.normalize_hex_id`` for tests that stub ``greenfloor_engine``.""" normalized = value.strip().lower() if normalized.startswith("0x"): normalized = normalized[2:] @@ -22,20 +22,20 @@ def mock_kernel_normalize_hex_id(value: str) -> str: return normalized -def mock_kernel_is_hex_id(value: str) -> bool: - return bool(mock_kernel_normalize_hex_id(value)) +def mock_engine_is_hex_id(value: str) -> bool: + return bool(mock_engine_normalize_hex_id(value)) -def mock_kernel_canonical_is_xch(asset_id: str) -> bool: +def mock_engine_canonical_is_xch(asset_id: str) -> bool: lowered = str(asset_id or "").strip().lower() return lowered in {"xch", "txch", "1"} -def mock_kernel_default_mojo_multiplier_for_asset(asset_id: str) -> int: - return 1_000_000_000_000 if mock_kernel_canonical_is_xch(asset_id) else 1_000 +def mock_engine_default_mojo_multiplier_for_asset(asset_id: str) -> int: + return 1_000_000_000_000 if mock_engine_canonical_is_xch(asset_id) else 1_000 -def mock_kernel_bootstrap_block_error( +def mock_engine_bootstrap_block_error( bootstrap_status: str, bootstrap_reason: str, bootstrap_ready: bool, @@ -51,7 +51,7 @@ def mock_kernel_bootstrap_block_error( return None -def mock_kernel_dexie_offer_asset_expectation_error( +def mock_engine_dexie_offer_asset_expectation_error( offered: object, requested: object, expected_offered_asset_id: str, @@ -108,7 +108,7 @@ def _matches_row(row: object, *, expected_asset: str, expected_symbol: str) -> b return None -def mock_kernel_expected_publish_asset_fields( +def mock_engine_expected_publish_asset_fields( side: str, base_symbol: str, quote_asset: str, @@ -131,12 +131,12 @@ def mock_kernel_expected_publish_asset_fields( } -class MinimalSignerKernel: - """Base stub for tests that patch ``sys.modules['greenfloor_kernel']``. +class MinimalSignerEngine: + """Base stub for tests that patch ``sys.modules['greenfloor_engine']``. Subclass and override only the symbols your test exercises. Hex helpers, offer-build pricing helpers, and Dexie verification are provided by default - so CLI/offer tests do not need to enumerate every kernel export. + so CLI/offer tests do not need to enumerate every engine export. """ @staticmethod @@ -152,7 +152,7 @@ def mojo_multiplier_for_leg(pricing: object, field: str, asset_id: str) -> int: pricing_dict = pricing if isinstance(pricing, dict) else {} if field in pricing_dict: return int(pricing_dict[field]) - return mock_kernel_default_mojo_multiplier_for_asset(asset_id) + return mock_engine_default_mojo_multiplier_for_asset(asset_id) @staticmethod def resolve_offer_expiry_for_pricing(pricing: object) -> tuple[str, int]: @@ -164,13 +164,13 @@ def resolve_quote_price_for_pricing(pricing: object) -> float: pricing_dict = pricing if isinstance(pricing, dict) else {} return float(pricing_dict.get("fixed_quote_per_base", 1.0)) - bootstrap_block_error = staticmethod(mock_kernel_bootstrap_block_error) - expected_publish_asset_fields = staticmethod(mock_kernel_expected_publish_asset_fields) + bootstrap_block_error = staticmethod(mock_engine_bootstrap_block_error) + expected_publish_asset_fields = staticmethod(mock_engine_expected_publish_asset_fields) dexie_offer_asset_expectation_error = staticmethod( - mock_kernel_dexie_offer_asset_expectation_error + mock_engine_dexie_offer_asset_expectation_error ) - normalize_hex_id = staticmethod(mock_kernel_normalize_hex_id) - is_hex_id = staticmethod(mock_kernel_is_hex_id) - canonical_is_xch = staticmethod(mock_kernel_canonical_is_xch) - default_mojo_multiplier_for_asset = staticmethod(mock_kernel_default_mojo_multiplier_for_asset) + normalize_hex_id = staticmethod(mock_engine_normalize_hex_id) + is_hex_id = staticmethod(mock_engine_is_hex_id) + canonical_is_xch = staticmethod(mock_engine_canonical_is_xch) + default_mojo_multiplier_for_asset = staticmethod(mock_engine_default_mojo_multiplier_for_asset) diff --git a/tests/test_cancel_policy_kernel.py b/tests/test_cancel_policy_kernel.py index 3a9f068f..fe6003fe 100644 --- a/tests/test_cancel_policy_kernel.py +++ b/tests/test_cancel_policy_kernel.py @@ -1,4 +1,4 @@ -"""Cancel-policy kernel parity tests.""" +"""Cancel-policy engine parity tests.""" from __future__ import annotations diff --git a/tests/test_coin_ops_policy_parity.py b/tests/test_coin_ops_policy_parity.py index 35b2c7cd..78766935 100644 --- a/tests/test_coin_ops_policy_parity.py +++ b/tests/test_coin_ops_policy_parity.py @@ -35,11 +35,11 @@ ], ) def test_canonical_xch_parity_with_hex_utils(asset_id: str, expected_xch: bool) -> None: - from greenfloor.core.kernel_bridge import import_kernel + from greenfloor.core.engine_bridge import import_engine - kernel = import_kernel() + engine = import_engine() assert canonical_is_xch(asset_id) is expected_xch - assert bool(kernel.canonical_is_xch(asset_id)) is expected_xch + assert bool(engine.canonical_is_xch(asset_id)) is expected_xch if expected_xch: assert coin_op_min_amount_mojos(canonical_asset_id=asset_id) == 0 else: @@ -56,13 +56,13 @@ def test_canonical_xch_parity_with_hex_utils(asset_id: str, expected_xch: bool) ], ) def test_normalize_hex_id_parity_with_kernel(value: str, expected: str) -> None: - from greenfloor.core.kernel_bridge import import_kernel + from greenfloor.core.engine_bridge import import_engine - kernel = import_kernel() + engine = import_engine() assert normalize_hex_id(value) == expected - assert str(kernel.normalize_hex_id(value)) == expected + assert str(engine.normalize_hex_id(value)) == expected assert is_hex_id(value) is bool(expected) - assert bool(kernel.is_hex_id(value)) is bool(expected) + assert bool(engine.is_hex_id(value)) is bool(expected) @pytest.mark.parametrize( @@ -73,11 +73,11 @@ def test_normalize_hex_id_parity_with_kernel(value: str, expected: str) -> None: ], ) def test_default_mojo_multiplier_parity_with_kernel(asset_id: str, expected: int) -> None: - from greenfloor.core.kernel_bridge import import_kernel + from greenfloor.core.engine_bridge import import_engine - kernel = import_kernel() + engine = import_engine() assert default_mojo_multiplier_for_asset(asset_id) == expected - assert int(kernel.default_mojo_multiplier_for_asset(asset_id)) == expected + assert int(engine.default_mojo_multiplier_for_asset(asset_id)) == expected def test_coin_meets_min_amount_rejects_invalid_amount_type() -> None: diff --git a/tests/test_cycle_reseed.py b/tests/test_cycle_reseed.py index de928762..16de529d 100644 --- a/tests/test_cycle_reseed.py +++ b/tests/test_cycle_reseed.py @@ -1,4 +1,4 @@ -"""Cycle reseed kernel contract tests.""" +"""Cycle reseed engine contract tests.""" from __future__ import annotations diff --git a/tests/test_engine_bridge.py b/tests/test_engine_bridge.py new file mode 100644 index 00000000..bff42348 --- /dev/null +++ b/tests/test_engine_bridge.py @@ -0,0 +1,96 @@ +"""Tests for greenfloor.core.engine_bridge ADR 0010 import hygiene.""" + +from __future__ import annotations + +import importlib +import sys +from types import ModuleType +from typing import Any + +import pytest + +from greenfloor.core import engine_bridge + + +def test_import_signer_is_import_engine_alias() -> None: + assert engine_bridge.import_signer is engine_bridge.import_engine + + +def test_engine_rebuild_hint_uses_module_argument() -> None: + hint = engine_bridge.engine_rebuild_hint( + module=engine_bridge.ENGINE_MODULE, + missing="offer-request", + ) + assert engine_bridge.ENGINE_MODULE in hint + assert "maturin develop" in hint + assert "offer-request" in hint + + +def test_import_engine_loads_target_module(monkeypatch) -> None: + calls: list[str] = [] + + def _fake_import(name: str) -> ModuleType: + calls.append(name) + if name == engine_bridge.ENGINE_MODULE: + return ModuleType(name) + raise ImportError(f"missing {name}") + + monkeypatch.setattr(importlib, "import_module", _fake_import) + mod = engine_bridge.import_engine() + assert mod.__name__ == engine_bridge.ENGINE_MODULE + assert calls == [engine_bridge.ENGINE_MODULE] + + +def test_import_engine_error_lists_module(monkeypatch) -> None: + def _always_fail(name: str) -> ModuleType: + raise ImportError(f"missing {name}") + + monkeypatch.setattr(importlib, "import_module", _always_fail) + with pytest.raises(ImportError, match="greenfloor_engine") as exc_info: + engine_bridge.import_engine() + message = str(exc_info.value) + assert engine_bridge.ENGINE_MODULE in message + assert "maturin develop" in message + + +def test_require_engine_method_uses_loaded_module_name() -> None: + module = ModuleType(engine_bridge.ENGINE_MODULE) + with pytest.raises(RuntimeError, match=engine_bridge.ENGINE_MODULE) as exc_info: + engine_bridge.require_engine_method(module, "missing_symbol", missing="required policy") + assert "Missing symbol: missing_symbol" in str(exc_info.value) + assert "required policy" in str(exc_info.value) + + +def test_require_engine_method_with_sys_modules_stub(monkeypatch) -> None: + """Regression: policy bridges resolve symbols on test stubs without __spec__.""" + stub = ModuleType(engine_bridge.ENGINE_MODULE) + monkeypatch.setitem(sys.modules, engine_bridge.ENGINE_MODULE, stub) + with pytest.raises(RuntimeError, match="Missing symbol: bootstrap_block_error"): + engine_bridge.require_engine_method( + stub, + "bootstrap_block_error", + missing="required policy", + ) + + +def test_policy_coin_ops_and_bootstrap_engines_share_import(monkeypatch) -> None: + module = ModuleType(engine_bridge.ENGINE_MODULE) + monkeypatch.setattr(engine_bridge, "_loaded_engine_module", lambda: module) + assert engine_bridge.policy_engine() is module + assert engine_bridge.coin_ops_engine() is module + assert engine_bridge.bootstrap_engine() is module + + +def test_engine_method_getter_delegates_to_require_engine_method(monkeypatch) -> None: + module = ModuleType(engine_bridge.ENGINE_MODULE) + calls: list[tuple[Any, str, str]] = [] + sentinel = object() + + def _record_require(engine: Any, method_name: str, *, missing: str) -> object: + calls.append((engine, method_name, missing)) + return sentinel + + monkeypatch.setattr(engine_bridge, "require_engine_method", _record_require) + getter = engine_bridge.engine_method_getter(lambda: module, missing="offer-request") + assert getter("normalize_offer_side") is sentinel + assert calls == [(module, "normalize_offer_side", "offer-request")] diff --git a/tests/test_greenfloor_engine_integration.py b/tests/test_greenfloor_engine_integration.py index 21c58dc0..f1ad9975 100644 --- a/tests/test_greenfloor_engine_integration.py +++ b/tests/test_greenfloor_engine_integration.py @@ -1,4 +1,4 @@ -"""Integration tests for the greenfloor_kernel PyO3 extension.""" +"""Integration tests for the greenfloor_engine PyO3 extension.""" from __future__ import annotations @@ -9,9 +9,9 @@ import pytest -def _require_signer_integration_enabled() -> None: - if os.getenv("GREENFLOOR_RUN_SIGNER_INTEGRATION_TESTS", "").strip() != "1": - pytest.skip("set GREENFLOOR_RUN_SIGNER_INTEGRATION_TESTS=1 to run greenfloor-signer tests") +def _require_kernel_integration_enabled() -> None: + if os.getenv("GREENFLOOR_RUN_ENGINE_INTEGRATION_TESTS", "").strip() != "1": + pytest.skip("set GREENFLOOR_RUN_ENGINE_INTEGRATION_TESTS=1 to run greenfloor-engine tests") def _require_importable_modules(): @@ -20,37 +20,37 @@ def _require_importable_modules(): except Exception: pytest.skip("chia_wallet_sdk import unavailable") try: - import greenfloor_kernel as signer # type: ignore + import greenfloor_engine as engine # type: ignore except Exception: - pytest.skip("greenfloor_kernel import unavailable") - return sdk, signer + pytest.skip("greenfloor_engine import unavailable") + return sdk, engine -def test_greenfloor_signer_validate_offer_rejects_garbage() -> None: - _require_signer_integration_enabled() - _sdk, signer = _require_importable_modules() +def test_greenfloor_engine_validate_offer_rejects_garbage() -> None: + _require_kernel_integration_enabled() + _sdk, engine = _require_importable_modules() with pytest.raises(ValueError): - signer.validate_offer("not-an-offer") + engine.validate_offer("not-an-offer") -def test_greenfloor_signer_from_input_spend_bundle_xch_round_trip_offer() -> None: - _require_signer_integration_enabled() - sdk, signer = _require_importable_modules() +def test_greenfloor_engine_from_input_spend_bundle_xch_round_trip_offer() -> None: + _require_kernel_integration_enabled() + sdk, engine = _require_importable_modules() input_spend_bundle = sdk.SpendBundle([], sdk.Signature.infinity()) - offer_spend_bundle_bytes = signer.from_input_spend_bundle_xch( + offer_spend_bundle_bytes = engine.from_input_spend_bundle_xch( input_spend_bundle.to_bytes(), [(bytes([3]) * 32, [(bytes([4]) * 32, 42)])], ) - offer_text = signer.encode_offer(offer_spend_bundle_bytes) + offer_text = engine.encode_offer(offer_spend_bundle_bytes) assert str(offer_text).startswith("offer1") # Synthetic round-trip offers have no expiry; structure validation is the contract here. - signer.validate_offer_structure(offer_text) + engine.validate_offer_structure(offer_text) -def test_greenfloor_signer_config_yaml_roundtrip(tmp_path: Path) -> None: - _require_signer_integration_enabled() - _sdk, signer = _require_importable_modules() +def test_greenfloor_engine_config_yaml_roundtrip(tmp_path: Path) -> None: + _require_kernel_integration_enabled() + _sdk, engine = _require_importable_modules() program = tmp_path / "program.yaml" program.write_text( @@ -76,6 +76,6 @@ def test_greenfloor_signer_config_yaml_roundtrip(tmp_path: Path) -> None: """.strip(), encoding="utf-8", ) - context: Any = signer.resolve_vault_context(str(program)) + context: Any = engine.resolve_vault_context(str(program)) assert context["launcher_id"] == "aa" * 32 assert context["custody_threshold"] == 1 diff --git a/tests/test_greenfloor_engine_vault_hash_parity.py b/tests/test_greenfloor_engine_vault_hash_parity.py index 7e7b51c2..25b980a4 100644 --- a/tests/test_greenfloor_engine_vault_hash_parity.py +++ b/tests/test_greenfloor_engine_vault_hash_parity.py @@ -6,7 +6,7 @@ import pytest # Expected hash vectors are canonical in tests/fixtures/vault_hash_golden.json -# (mirrored by greenfloor-signer/src/test_support/golden.rs during migration). +# (mirrored by greenfloor-engine/src/test_support/golden.rs during migration). FIXTURE_PATH = Path(__file__).resolve().parent / "fixtures" / "vault_hash_golden.json" @@ -78,7 +78,7 @@ def _compute_vault_hashes(fixture: dict[str, str]) -> dict[str, bytes]: } -def test_greenfloor_signer_vault_hash_parity_matches_python_sdk() -> None: +def test_greenfloor_engine_vault_hash_parity_matches_python_sdk() -> None: fixture = _load_golden_fixture() hashes = _compute_vault_hashes(fixture) assert hashes["inner_puzzle_hash"].hex() == fixture["inner_puzzle_hash"] diff --git a/tests/test_greenfloor_native_contract.py b/tests/test_greenfloor_native_contract.py index 830d8242..dec6b3cb 100644 --- a/tests/test_greenfloor_native_contract.py +++ b/tests/test_greenfloor_native_contract.py @@ -1,10 +1,10 @@ from __future__ import annotations from greenfloor.core.offer_policy import verify_offer_for_dexie -from tests.helpers.kernel_mock import install_kernel_stub +from tests.helpers.engine_mock import install_engine_stub -def test_verify_offer_for_dexie_uses_greenfloor_kernel_only(monkeypatch) -> None: +def test_verify_offer_for_dexie_uses_greenfloor_engine_only(monkeypatch) -> None: calls: dict[str, str] = {} class _Signer: @@ -12,7 +12,7 @@ class _Signer: def verify_offer_for_dexie(offer: str) -> None: calls["offer"] = offer - install_kernel_stub(monkeypatch, _Signer) + install_engine_stub(monkeypatch, _Signer) assert verify_offer_for_dexie("offer1contract") is None assert calls["offer"] == "offer1contract" @@ -20,10 +20,10 @@ def verify_offer_for_dexie(offer: str) -> None: def test_verify_offer_for_dexie_reports_missing_kernel(monkeypatch) -> None: monkeypatch.setattr( - "greenfloor.core.kernel_bridge.import_kernel", + "greenfloor.core.engine_bridge.import_engine", lambda: (_ for _ in ()).throw(ImportError("disable signer path for this test")), ) assert verify_offer_for_dexie("offer1contract") == ( - "wallet_sdk_import_error:greenfloor_kernel_unavailable" + "wallet_sdk_import_error:greenfloor_engine_unavailable" ) diff --git a/tests/test_kernel_bridge.py b/tests/test_kernel_bridge.py deleted file mode 100644 index fe525a3c..00000000 --- a/tests/test_kernel_bridge.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Tests for greenfloor.core.kernel_bridge ADR 0010 import hygiene.""" - -from __future__ import annotations - -import importlib -import sys -from types import ModuleType -from typing import Any - -import pytest - -from greenfloor.core import kernel_bridge - - -def test_import_signer_is_import_kernel_alias() -> None: - assert kernel_bridge.import_signer is kernel_bridge.import_kernel - - -def test_kernel_rebuild_hint_uses_module_argument() -> None: - hint = kernel_bridge.kernel_rebuild_hint( - module=kernel_bridge.KERNEL_MODULE, - missing="offer-request", - ) - assert kernel_bridge.KERNEL_MODULE in hint - assert "maturin develop" in hint - assert "offer-request" in hint - - -def test_import_kernel_loads_target_module(monkeypatch) -> None: - calls: list[str] = [] - - def _fake_import(name: str) -> ModuleType: - calls.append(name) - if name == kernel_bridge.KERNEL_MODULE: - return ModuleType(name) - raise ImportError(f"missing {name}") - - monkeypatch.setattr(importlib, "import_module", _fake_import) - mod = kernel_bridge.import_kernel() - assert mod.__name__ == kernel_bridge.KERNEL_MODULE - assert calls == [kernel_bridge.KERNEL_MODULE] - - -def test_import_kernel_error_lists_module(monkeypatch) -> None: - def _always_fail(name: str) -> ModuleType: - raise ImportError(f"missing {name}") - - monkeypatch.setattr(importlib, "import_module", _always_fail) - with pytest.raises(ImportError, match="greenfloor_kernel") as exc_info: - kernel_bridge.import_kernel() - message = str(exc_info.value) - assert kernel_bridge.KERNEL_MODULE in message - assert "maturin develop" in message - - -def test_require_kernel_method_uses_loaded_module_name() -> None: - module = ModuleType(kernel_bridge.KERNEL_MODULE) - with pytest.raises(RuntimeError, match=kernel_bridge.KERNEL_MODULE) as exc_info: - kernel_bridge.require_kernel_method(module, "missing_symbol", missing="required policy") - assert "Missing symbol: missing_symbol" in str(exc_info.value) - assert "required policy" in str(exc_info.value) - - -def test_require_kernel_method_with_sys_modules_stub(monkeypatch) -> None: - """Regression: policy bridges resolve symbols on test stubs without __spec__.""" - stub = ModuleType(kernel_bridge.KERNEL_MODULE) - monkeypatch.setitem(sys.modules, kernel_bridge.KERNEL_MODULE, stub) - with pytest.raises(RuntimeError, match="Missing symbol: bootstrap_block_error"): - kernel_bridge.require_kernel_method( - stub, - "bootstrap_block_error", - missing="required policy", - ) - - -def test_policy_coin_ops_and_bootstrap_kernels_share_import(monkeypatch) -> None: - module = ModuleType(kernel_bridge.KERNEL_MODULE) - monkeypatch.setattr(kernel_bridge, "_loaded_kernel_module", lambda: module) - assert kernel_bridge.policy_kernel() is module - assert kernel_bridge.coin_ops_kernel() is module - assert kernel_bridge.bootstrap_kernel() is module - - -def test_kernel_method_getter_delegates_to_require_kernel_method(monkeypatch) -> None: - module = ModuleType(kernel_bridge.KERNEL_MODULE) - calls: list[tuple[Any, str, str]] = [] - sentinel = object() - - def _record_require(kernel: Any, method_name: str, *, missing: str) -> object: - calls.append((kernel, method_name, missing)) - return sentinel - - monkeypatch.setattr(kernel_bridge, "require_kernel_method", _record_require) - getter = kernel_bridge.kernel_method_getter(lambda: module, missing="offer-request") - assert getter("normalize_offer_side") is sentinel - assert calls == [(module, "normalize_offer_side", "offer-request")] diff --git a/tests/test_manager_build_post_offer.py b/tests/test_manager_build_post_offer.py index c85fc586..1f7c14e4 100644 --- a/tests/test_manager_build_post_offer.py +++ b/tests/test_manager_build_post_offer.py @@ -510,18 +510,18 @@ def test_build_and_post_offer_returns_nonzero_when_offer_verification_fails( def test_build_and_post_offer_surfaces_stale_kernel_symbol_as_user_error( monkeypatch, tmp_path: Path, capsys ) -> None: - from tests.helpers.kernel_mock import MinimalSignerKernel + from tests.helpers.engine_mock import MinimalSignerEngine program = tmp_path / "program.yaml" markets = tmp_path / "markets.yaml" write_manager_program(program, tmp_path=tmp_path) write_markets(markets) - class _StaleKernel(MinimalSignerKernel): + class _StaleKernel(MinimalSignerEngine): expected_publish_asset_fields = None monkeypatch.setattr( - "greenfloor.core.kernel_bridge.import_kernel", + "greenfloor.core.engine_bridge.import_engine", lambda: _StaleKernel, ) monkeypatch.setattr( @@ -574,14 +574,14 @@ def post_offer(self, offer: str, *, drop_only: bool, claim_rewards: bool | None) called["post_offer_called"] = True return {"success": True, "id": "should-not-post"} - from tests.helpers.kernel_mock import MinimalSignerKernel, install_kernel_stub + from tests.helpers.engine_mock import MinimalSignerEngine, install_engine_stub - class _Signer(MinimalSignerKernel): + class _Signer(MinimalSignerEngine): @staticmethod def verify_offer_for_dexie(_offer: str) -> str: return "wallet_sdk_offer_missing_expiration" - install_kernel_stub(monkeypatch, _Signer) + install_engine_stub(monkeypatch, _Signer) monkeypatch.setattr( "greenfloor.cli.offer_build_post.build_offer", lambda _payload: "offer1noexpiry", diff --git a/tests/test_offer_bootstrap.py b/tests/test_offer_bootstrap.py index f06f5f43..e7664952 100644 --- a/tests/test_offer_bootstrap.py +++ b/tests/test_offer_bootstrap.py @@ -113,12 +113,12 @@ def test_plan_bootstrap_mixed_outputs_rejects_non_string_coin_id() -> None: def test_plan_bootstrap_mixed_outputs_requires_kernel_symbol(monkeypatch) -> None: - import greenfloor.core.kernel_bridge as bridge + import greenfloor.core.engine_bridge as bridge class _Kernel: pass - monkeypatch.setattr(bridge, "bootstrap_kernel", lambda: _Kernel()) + monkeypatch.setattr(bridge, "bootstrap_engine", lambda: _Kernel()) with pytest.raises(RuntimeError, match="plan_bootstrap_mixed_outputs"): plan_bootstrap_mixed_outputs(ladder_entries=_sample_ladder(), spendable_coins=[]) diff --git a/tests/test_offer_publish.py b/tests/test_offer_publish.py index 2df42bbe..6d94747f 100644 --- a/tests/test_offer_publish.py +++ b/tests/test_offer_publish.py @@ -17,61 +17,61 @@ post_offer_phase, verify_dexie_offer_visible_by_id, ) -from tests.helpers.kernel_mock import MinimalSignerKernel, install_kernel_stub +from tests.helpers.engine_mock import MinimalSignerEngine, install_engine_stub def test_verify_offer_for_dexie_success(monkeypatch) -> None: calls: list[str] = [] - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def verify_offer_for_dexie(offer: str) -> None: calls.append(offer) - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert verify_offer_for_dexie("offer1ok") is None assert calls == ["offer1ok"] def test_verify_offer_for_dexie_maps_duplicate_spends(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def verify_offer_for_dexie(_offer: str) -> str: return "wallet_sdk_offer_duplicate_spent_coin_ids" - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert verify_offer_for_dexie("offer1duplicate") == "wallet_sdk_offer_duplicate_spent_coin_ids" def test_verify_offer_for_dexie_maps_missing_expiration(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def verify_offer_for_dexie(_offer: str) -> str: return "wallet_sdk_offer_missing_expiration" - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert verify_offer_for_dexie("offer1noexpiry") == "wallet_sdk_offer_missing_expiration" def test_verify_offer_for_dexie_returns_native_validation_error(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def verify_offer_for_dexie(_offer: str) -> str: return "wallet_sdk_offer_validate_failed:native_invalid_offer" - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert verify_offer_for_dexie("offer1bad") == ( "wallet_sdk_offer_validate_failed:native_invalid_offer" ) def test_verify_offer_for_dexie_maps_structure_validate_failed(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def verify_offer_for_dexie(_offer: str) -> str: return "wallet_sdk_offer_validate_failed:malformed_offer" - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert verify_offer_for_dexie("offer1malformed") == ( "wallet_sdk_offer_validate_failed:malformed_offer" ) @@ -79,16 +79,16 @@ def verify_offer_for_dexie(_offer: str) -> str: def test_verify_offer_for_dexie_reports_missing_kernel(monkeypatch) -> None: monkeypatch.setattr( - "greenfloor.core.kernel_bridge.import_kernel", - lambda: (_ for _ in ()).throw(ImportError("greenfloor_kernel_unavailable")), + "greenfloor.core.engine_bridge.import_engine", + lambda: (_ for _ in ()).throw(ImportError("greenfloor_engine_unavailable")), ) assert verify_offer_for_dexie("offer1contract") == ( - "wallet_sdk_import_error:greenfloor_kernel_unavailable" + "wallet_sdk_import_error:greenfloor_engine_unavailable" ) def test_bootstrap_block_error_delegates_to_kernel(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def bootstrap_block_error( bootstrap_status: str, @@ -98,7 +98,7 @@ def bootstrap_block_error( _ = bootstrap_ready return f"kernel_bootstrap:{bootstrap_status}:{bootstrap_reason}" - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert ( bootstrap_block_error( bootstrap_status="failed", @@ -113,7 +113,7 @@ def test_bootstrap_block_error_requires_kernel_symbol(monkeypatch) -> None: class _Native: pass - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) with pytest.raises(RuntimeError, match="Missing symbol: bootstrap_block_error"): bootstrap_block_error( bootstrap_status="executed", @@ -123,7 +123,7 @@ class _Native: def test_expected_publish_asset_fields_delegates_to_kernel(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def expected_publish_asset_fields( side: str, @@ -139,7 +139,7 @@ def expected_publish_asset_fields( "expected_requested_symbol": base_symbol, } - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert expected_publish_asset_fields( side="buy", base_symbol="A1", @@ -158,7 +158,7 @@ def test_expected_publish_asset_fields_requires_kernel_symbol(monkeypatch) -> No class _Native: pass - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) with pytest.raises(RuntimeError, match="Missing symbol: expected_publish_asset_fields"): expected_publish_asset_fields( side="buy", @@ -170,7 +170,7 @@ class _Native: def test_expected_publish_asset_fields_requires_complete_payload(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def expected_publish_asset_fields( _side: str, @@ -185,7 +185,7 @@ def expected_publish_asset_fields( "expected_requested_symbol": "A1", } - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) with pytest.raises( TypeError, match="expected_publish_asset_fields missing keys: expected_offered_symbol", @@ -200,7 +200,7 @@ def expected_publish_asset_fields( def test_resolve_offer_expiry_and_quote_price_use_kernel(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def resolve_offer_expiry_for_pricing(_pricing): return ("minutes", 12) @@ -209,14 +209,14 @@ def resolve_offer_expiry_for_pricing(_pricing): def resolve_quote_price_for_pricing(_pricing): return 1.5 - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) pricing = {"strategy_offer_expiry_minutes": 12} assert resolve_offer_expiry_for_pricing(pricing) == ("minutes", 12) assert resolve_quote_price_for_pricing(pricing) == 1.5 def test_dexie_offer_asset_expectation_error_delegates_to_kernel(monkeypatch) -> None: - class _Native(MinimalSignerKernel): + class _Native(MinimalSignerEngine): @staticmethod def dexie_offer_asset_expectation_error( offered: object, @@ -235,7 +235,7 @@ def dexie_offer_asset_expectation_error( f"offered_symbol={expected_offered_symbol}" ) - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) assert dexie_offer_asset_expectation_error( offered=[], requested=[], @@ -253,7 +253,7 @@ def test_dexie_offer_asset_expectation_error_requires_kernel_symbol(monkeypatch) class _Native: pass - install_kernel_stub(monkeypatch, _Native) + install_engine_stub(monkeypatch, _Native) with pytest.raises(RuntimeError, match="Missing symbol: dexie_offer_asset_expectation_error"): dexie_offer_asset_expectation_error( offered=[], diff --git a/tests/test_signer_create_offer_parity.py b/tests/test_signer_create_offer_parity.py index 2d54a413..9f74aa28 100644 --- a/tests/test_signer_create_offer_parity.py +++ b/tests/test_signer_create_offer_parity.py @@ -32,16 +32,16 @@ ) -def _require_signer_kernel(): +def _require_signer_engine(): try: - import greenfloor_kernel as kernel # type: ignore[import-not-found] + import greenfloor_engine as engine # type: ignore[import-not-found] except ImportError: - pytest.skip("greenfloor_kernel not installed") - if not callable(getattr(kernel, "compute_signer_offer_leg_amounts", None)): - pytest.skip("greenfloor_kernel.compute_signer_offer_leg_amounts not available") - if not callable(getattr(kernel, "normalize_offer_side", None)): - pytest.skip("greenfloor_kernel.normalize_offer_side not available") - return kernel + pytest.skip("greenfloor_engine not installed") + if not callable(getattr(engine, "compute_signer_offer_leg_amounts", None)): + pytest.skip("greenfloor_engine.compute_signer_offer_leg_amounts not available") + if not callable(getattr(engine, "normalize_offer_side", None)): + pytest.skip("greenfloor_engine.normalize_offer_side not available") + return engine @pytest.mark.parametrize( @@ -53,17 +53,17 @@ def _require_signer_kernel(): ("", "sell"), ], ) -def test_normalize_offer_side_matches_kernel(raw: str, expected: str) -> None: - kernel = _require_signer_kernel() +def test_normalize_offer_side_matches_engine(raw: str, expected: str) -> None: + engine = _require_signer_engine() assert normalize_offer_side(raw) == expected - kernel_normalize = cast( + engine_normalize = cast( Callable[[str], str], - kernel.normalize_offer_side, # pyright: ignore[reportAttributeAccessIssue] + engine.normalize_offer_side, # pyright: ignore[reportAttributeAccessIssue] ) - assert str(kernel_normalize(str(raw))) == expected + assert str(engine_normalize(str(raw))) == expected -def test_planned_action_side_avoids_kernel_for_canonical_labels() -> None: +def test_planned_action_side_avoids_engine_for_canonical_labels() -> None: action = PlannedAction( size=1, repeat=1, @@ -78,6 +78,7 @@ def test_planned_action_side_avoids_kernel_for_canonical_labels() -> None: def test_prepare_offer_build_context_caches_normalized_side(tmp_path: Path) -> None: + _require_signer_engine() program = program_config_for_local_offer(home_dir=str(tmp_path)) market = replace( market_config_for_local_offer(), @@ -96,7 +97,7 @@ def test_prepare_offer_build_context_caches_normalized_side(tmp_path: Path) -> N @pytest.mark.parametrize("fixture_path", sorted(SIGNER_FIXTURE_DIR.glob("*.json"))) def test_signer_golden_fixture_contract(fixture_path: Path) -> None: - _require_signer_kernel() + _require_signer_engine() payload = json.loads(fixture_path.read_text(encoding="utf-8")) fixture_req, expires_at_unix = parse_create_offer_request(payload["create_offer_request"]) parity = parse_runtime_parity(payload["runtime_parity"]) @@ -155,7 +156,7 @@ def test_compute_signer_offer_leg_amounts_rejects_invalid_inputs( quote_price: float, match: str, ) -> None: - _require_signer_kernel() + _require_signer_engine() pricing = { "base_unit_mojo_multiplier": 1000, "quote_unit_mojo_multiplier": 1000, @@ -173,13 +174,13 @@ def test_compute_signer_offer_leg_amounts_rejects_invalid_inputs( ) -@pytest.mark.signer +@pytest.mark.engine @pytest.mark.parametrize("fixture_path", sorted(SIGNER_FIXTURE_DIR.glob("*.json"))) def test_signer_golden_offer_validates(fixture_path: Path) -> None: - kernel = _require_signer_kernel() - validate = getattr(kernel, "validate_offer_structure", None) + engine = _require_signer_engine() + validate = getattr(engine, "validate_offer_structure", None) if not callable(validate): - pytest.skip("greenfloor_kernel.validate_offer_structure not available") + pytest.skip("greenfloor_engine.validate_offer_structure not available") payload = json.loads(fixture_path.read_text(encoding="utf-8")) offer = str(payload.get("offer", "")).strip() diff --git a/tests/test_signing.py b/tests/test_signing.py index a938ef95..d23669fd 100644 --- a/tests/test_signing.py +++ b/tests/test_signing.py @@ -158,14 +158,14 @@ def test_build_signed_spend_bundle_invalid_plan(monkeypatch) -> None: def test_build_signed_spend_bundle_signer_import_error(monkeypatch) -> None: def _fail_import(): - raise ImportError("no greenfloor_kernel") + raise ImportError("no greenfloor_engine") monkeypatch.setattr( signing_mod, "_load_master_private_key", lambda *_args, **_kwargs: (b"\x01" * 32, None), ) - monkeypatch.setattr(signing_mod, "import_kernel", _fail_import) + monkeypatch.setattr(signing_mod, "import_engine", _fail_import) result = signing_mod.build_signed_spend_bundle( { "key_id": "k1", @@ -177,7 +177,7 @@ def _fail_import(): } ) assert result["status"] == "skipped" - assert result["reason"] == "signing_failed:greenfloor_kernel_import_error:no greenfloor_kernel" + assert result["reason"] == "signing_failed:greenfloor_engine_import_error:no greenfloor_engine" def test_build_signed_spend_bundle_no_coins(monkeypatch) -> None: @@ -493,7 +493,7 @@ class _NotarizedPayment: nonce = b"\x22" * 32 payments = [_Payment()] - monkeypatch.setattr(native_offer_mod, "import_kernel", lambda: _Native) + monkeypatch.setattr(native_offer_mod, "import_engine", lambda: _Native) result = native_offer_mod.from_input_spend_bundle_xch( sdk=_Sdk, @@ -541,7 +541,7 @@ class _NotarizedPayment: nonce = _ByteWrapper(b"\xaa" * 32) payments = [_Payment()] - monkeypatch.setattr(native_offer_mod, "import_kernel", lambda: _Native) + monkeypatch.setattr(native_offer_mod, "import_engine", lambda: _Native) result = native_offer_mod.from_input_spend_bundle_xch( sdk=_Sdk, @@ -576,7 +576,7 @@ class _NotarizedPayment: nonce = b"\x22" * 32 payments = [_Payment()] - monkeypatch.setattr(native_offer_mod, "import_kernel", lambda: _Native) + monkeypatch.setattr(native_offer_mod, "import_engine", lambda: _Native) try: native_offer_mod.from_input_spend_bundle_xch( @@ -806,7 +806,7 @@ class _Signer: def build_bls_mixed_split(_network: str, _sk: bytes, _request: dict) -> dict: return {"error": "cat_output_below_minimum_mojos"} - monkeypatch.setattr(signing_mod, "import_kernel", lambda: _Signer()) + monkeypatch.setattr(signing_mod, "import_engine", lambda: _Signer()) spend_bundle_hex, err = signing_mod._build_mixed_split_spend_bundle( { From 9eab81d50654509b20ffb06df9a9afa4a9254d73 Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Fri, 29 May 2026 15:27:25 -0700 Subject: [PATCH 3/4] Keep Cargo build output in repo target Co-authored-by: Cursor --- .cargo/config.toml | 2 ++ .gitignore | 1 + 2 files changed, 3 insertions(+) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..a6b014e9 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target-dir = "target" diff --git a/.gitignore b/.gitignore index 4ef36451..a7b7f74c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ __pycache__/ dist/ build/ *.egg-info/ +target/ greenfloor-native/target/ greenfloor-engine-pyo3/target/ greenfloor-engine-pyo3/python/ From fec012a0b21064f5fbc986cb85d42dcf15f813ab Mon Sep 17 00:00:00 2001 From: Gene Hoffman Date: Fri, 29 May 2026 15:30:12 -0700 Subject: [PATCH 4/4] Drop legacy kernel naming shims Co-authored-by: Cursor --- docs/progress.md | 4 +- greenfloor/core/coin_ops/kernel_protocol.py | 7 ---- greenfloor/core/kernel_bridge.py | 38 ------------------- greenfloor/core/kernel_maps.py | 5 --- greenfloor/core/kernel_protocol.py | 38 ------------------- ...kernel.py => test_cancel_policy_engine.py} | 0 tests/test_coin_ops_gate_parity.py | 2 +- tests/test_coin_ops_policy_parity.py | 4 +- tests/test_cycle_reseed.py | 2 +- tests/test_greenfloor_native_contract.py | 2 +- tests/test_manager_build_post_offer.py | 6 +-- tests/test_offer_action_build.py | 2 +- tests/test_offer_bootstrap.py | 2 +- tests/test_offer_orchestration.py | 2 +- tests/test_offer_publish.py | 16 ++++---- ...rnel.py => test_offer_reconcile_engine.py} | 0 tests/test_retry_policy_parity.py | 2 +- 17 files changed, 22 insertions(+), 110 deletions(-) delete mode 100644 greenfloor/core/coin_ops/kernel_protocol.py delete mode 100644 greenfloor/core/kernel_bridge.py delete mode 100644 greenfloor/core/kernel_maps.py delete mode 100644 greenfloor/core/kernel_protocol.py rename tests/{test_cancel_policy_kernel.py => test_cancel_policy_engine.py} (100%) rename tests/{test_offer_reconcile_kernel.py => test_offer_reconcile_engine.py} (100%) diff --git a/docs/progress.md b/docs/progress.md index 856aa863..28e1554a 100644 --- a/docs/progress.md +++ b/docs/progress.md @@ -39,7 +39,7 @@ - **Bootstrap block gate:** moved `bootstrap_blocks_offer` decision shaping into `greenfloor-engine/src/offer/publish.rs` (`bootstrap_block_error`); Python orchestration delegates via `core.offer_policy`. - **Publish asset-field shaping:** moved `expected_publish_asset_fields` mapping into `greenfloor-engine/src/offer/publish.rs`; orchestration now calls `core.offer_policy.expected_publish_asset_fields` directly. - **Offer policy bridge contract:** removed silent Python fallbacks for migrated offer policy helpers; bridge now requires engine symbols and fails fast with rebuild guidance when extensions are stale. -- **CLI stale-symbol UX:** `runtime/offer_orchestration.py` now catches offer-policy bridge exceptions and emits structured `offer_policy_error:*` result payloads; `test_build_and_post_offer_surfaces_stale_kernel_symbol_as_user_error` proves the manager CLI path returns a user-facing failure string. +- **CLI stale-symbol UX:** `runtime/offer_orchestration.py` now catches offer-policy bridge exceptions and emits structured `offer_policy_error:*` result payloads; `test_build_and_post_offer_surfaces_stale_engine_symbol_as_user_error` proves the manager CLI path returns a user-facing failure string. - **Rust tests:** `offer_leg_scenarios_build_on_simulator` (renamed from roundtrip; build-only on sim for CAT:CAT legs). - **Python tests:** `test_signer_golden_fixture_contract` (schema + core parity + validate in one parametrized test); `test_offer_runtime.py` covers signer IO shell only (leg math owned by parity fixtures). - **Fixtures:** all golden JSON embed `runtime_parity` (action_side, resolved assets, size/price, multipliers). @@ -171,7 +171,7 @@ Record these for the next migration agent; none are merge blockers for step 12. - **`greenfloor-engine/src/cycle/reconcile.rs`:** Coinset-first watched-offer transition engine — Dexie status fallback, missing-offer (404) handling, taker field shaping, and `CycleOfferTransition` outputs. - **PyO3 + core surface:** typed `CycleOfferTransition` via `reconcile_py.rs` + `py_utils.rs`; policy in `greenfloor/core/offer_reconcile/`. - **Python IO glue:** `greenfloor/runtime/offer_reconciliation.py` keeps Dexie fetch, SQLite tx-signal lookup (`_coinset_signal_lists`), audit persistence, and batch reconcile loops only. -- **Tests:** Rust unit tests in `reconcile.rs`; Python wiring in `tests/test_offer_reconcile_kernel.py`; existing manager/daemon reconcile integration tests remain parity gates. +- **Tests:** Rust unit tests in `reconcile.rs`; Python wiring in `tests/test_offer_reconcile_engine.py`; existing manager/daemon reconcile integration tests remain parity gates. - **Migration status:** step 9 complete for offer lifecycle reconciliation policy. - **Completed (step 10):** core coin-op policy bundle — see step 10 entry above (`greenfloor-engine/src/coin_ops/`, `greenfloor/core/coin_ops/`). diff --git a/greenfloor/core/coin_ops/kernel_protocol.py b/greenfloor/core/coin_ops/kernel_protocol.py deleted file mode 100644 index d31eb02f..00000000 --- a/greenfloor/core/coin_ops/kernel_protocol.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Compatibility shim for coin-op engine protocol names.""" - -from greenfloor.core.coin_ops.engine_protocol import CoinOpsEngineProtocol - -CoinOpsKernelProtocol = CoinOpsEngineProtocol - -__all__ = ["CoinOpsKernelProtocol"] diff --git a/greenfloor/core/kernel_bridge.py b/greenfloor/core/kernel_bridge.py deleted file mode 100644 index 558db69f..00000000 --- a/greenfloor/core/kernel_bridge.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Compatibility shim for the renamed engine bridge. - -New code should import from :mod:`greenfloor.core.engine_bridge`. -""" - -from greenfloor.core.engine_bridge import ( - ENGINE_MODULE as KERNEL_MODULE, -) -from greenfloor.core.engine_bridge import ( - bootstrap_engine, - coin_ops_engine, - engine_method_getter, - engine_rebuild_hint, - import_engine, - import_signer, - policy_engine, - require_engine_method, -) - -import_kernel = import_engine -policy_kernel = policy_engine -coin_ops_kernel = coin_ops_engine -bootstrap_kernel = bootstrap_engine -kernel_method_getter = engine_method_getter -kernel_rebuild_hint = engine_rebuild_hint -require_kernel_method = require_engine_method - -__all__ = [ - "KERNEL_MODULE", - "bootstrap_kernel", - "coin_ops_kernel", - "import_kernel", - "import_signer", - "kernel_method_getter", - "kernel_rebuild_hint", - "policy_kernel", - "require_kernel_method", -] diff --git a/greenfloor/core/kernel_maps.py b/greenfloor/core/kernel_maps.py deleted file mode 100644 index 65f9b4e7..00000000 --- a/greenfloor/core/kernel_maps.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Compatibility shim for engine map coercion helpers.""" - -from greenfloor.core.engine_maps import require_i64_i64_map, require_side_offer_count_maps - -__all__ = ["require_i64_i64_map", "require_side_offer_count_maps"] diff --git a/greenfloor/core/kernel_protocol.py b/greenfloor/core/kernel_protocol.py deleted file mode 100644 index a285082e..00000000 --- a/greenfloor/core/kernel_protocol.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Compatibility shim for engine protocol names. - -New code should import from :mod:`greenfloor.core.engine_protocol`. -""" - -from greenfloor.core.engine_protocol import ( - BootstrapEngineProtocol, - CancelPolicyEngineProtocol, - CoinOpsEngineProtocol, - CycleEngineProtocol, - DeterministicPolicyEngineProtocol, - NotificationEngineProtocol, - OfferPolicyEngineProtocol, - PolicyEngineProtocol, - RetryPolicyEngineProtocol, -) - -BootstrapKernelProtocol = BootstrapEngineProtocol -CancelPolicyKernelProtocol = CancelPolicyEngineProtocol -CoinOpsKernelProtocol = CoinOpsEngineProtocol -CycleKernelProtocol = CycleEngineProtocol -DeterministicPolicyKernelProtocol = DeterministicPolicyEngineProtocol -NotificationKernelProtocol = NotificationEngineProtocol -OfferPolicyKernelProtocol = OfferPolicyEngineProtocol -PolicyKernelProtocol = PolicyEngineProtocol -RetryPolicyKernelProtocol = RetryPolicyEngineProtocol - -__all__ = [ - "BootstrapKernelProtocol", - "CancelPolicyKernelProtocol", - "CoinOpsKernelProtocol", - "CycleKernelProtocol", - "DeterministicPolicyKernelProtocol", - "NotificationKernelProtocol", - "OfferPolicyKernelProtocol", - "PolicyKernelProtocol", - "RetryPolicyKernelProtocol", -] diff --git a/tests/test_cancel_policy_kernel.py b/tests/test_cancel_policy_engine.py similarity index 100% rename from tests/test_cancel_policy_kernel.py rename to tests/test_cancel_policy_engine.py diff --git a/tests/test_coin_ops_gate_parity.py b/tests/test_coin_ops_gate_parity.py index aa81fa73..69f1aa87 100644 --- a/tests/test_coin_ops_gate_parity.py +++ b/tests/test_coin_ops_gate_parity.py @@ -23,7 +23,7 @@ ) -def test_is_spendable_coin_matches_kernel() -> None: +def test_is_spendable_coin_matches_engine() -> None: coin = {"amount": 100, "state": "CONFIRMED"} assert is_spendable_coin(coin) is is_spendable_wallet_coin(coin) locked = {"amount": 100, "state": "CONFIRMED", "isLocked": True} diff --git a/tests/test_coin_ops_policy_parity.py b/tests/test_coin_ops_policy_parity.py index 78766935..e467b91a 100644 --- a/tests/test_coin_ops_policy_parity.py +++ b/tests/test_coin_ops_policy_parity.py @@ -55,7 +55,7 @@ def test_canonical_xch_parity_with_hex_utils(asset_id: str, expected_xch: bool) ("g" * 64, ""), ], ) -def test_normalize_hex_id_parity_with_kernel(value: str, expected: str) -> None: +def test_normalize_hex_id_parity_with_engine(value: str, expected: str) -> None: from greenfloor.core.engine_bridge import import_engine engine = import_engine() @@ -72,7 +72,7 @@ def test_normalize_hex_id_parity_with_kernel(value: str, expected: str) -> None: (_CAT_ID, 1_000), ], ) -def test_default_mojo_multiplier_parity_with_kernel(asset_id: str, expected: int) -> None: +def test_default_mojo_multiplier_parity_with_engine(asset_id: str, expected: int) -> None: from greenfloor.core.engine_bridge import import_engine engine = import_engine() diff --git a/tests/test_cycle_reseed.py b/tests/test_cycle_reseed.py index 16de529d..c96dcf0a 100644 --- a/tests/test_cycle_reseed.py +++ b/tests/test_cycle_reseed.py @@ -8,7 +8,7 @@ from greenfloor.core.cycle_reseed import ReseedSkipReason, python_reseed_skip_reason_labels -def test_reseed_skip_reason_labels_match_rust_kernel() -> None: +def test_reseed_skip_reason_labels_match_rust_engine() -> None: rust_labels = frozenset(rust_reseed_skip_reason_labels()) python_labels = python_reseed_skip_reason_labels() assert rust_labels == python_labels diff --git a/tests/test_greenfloor_native_contract.py b/tests/test_greenfloor_native_contract.py index dec6b3cb..c32d46db 100644 --- a/tests/test_greenfloor_native_contract.py +++ b/tests/test_greenfloor_native_contract.py @@ -18,7 +18,7 @@ def verify_offer_for_dexie(offer: str) -> None: assert calls["offer"] == "offer1contract" -def test_verify_offer_for_dexie_reports_missing_kernel(monkeypatch) -> None: +def test_verify_offer_for_dexie_reports_missing_engine(monkeypatch) -> None: monkeypatch.setattr( "greenfloor.core.engine_bridge.import_engine", lambda: (_ for _ in ()).throw(ImportError("disable signer path for this test")), diff --git a/tests/test_manager_build_post_offer.py b/tests/test_manager_build_post_offer.py index 1f7c14e4..5f1252aa 100644 --- a/tests/test_manager_build_post_offer.py +++ b/tests/test_manager_build_post_offer.py @@ -507,7 +507,7 @@ def test_build_and_post_offer_returns_nonzero_when_offer_verification_fails( assert payload["results"][0]["result"]["success"] is False -def test_build_and_post_offer_surfaces_stale_kernel_symbol_as_user_error( +def test_build_and_post_offer_surfaces_stale_engine_symbol_as_user_error( monkeypatch, tmp_path: Path, capsys ) -> None: from tests.helpers.engine_mock import MinimalSignerEngine @@ -517,12 +517,12 @@ def test_build_and_post_offer_surfaces_stale_kernel_symbol_as_user_error( write_manager_program(program, tmp_path=tmp_path) write_markets(markets) - class _StaleKernel(MinimalSignerEngine): + class _StaleEngine(MinimalSignerEngine): expected_publish_asset_fields = None monkeypatch.setattr( "greenfloor.core.engine_bridge.import_engine", - lambda: _StaleKernel, + lambda: _StaleEngine, ) monkeypatch.setattr( "greenfloor.cli.offer_build_post.build_offer", diff --git a/tests/test_offer_action_build.py b/tests/test_offer_action_build.py index 2bf29ce8..80257769 100644 --- a/tests/test_offer_action_build.py +++ b/tests/test_offer_action_build.py @@ -13,7 +13,7 @@ ) -def test_resolve_action_assets_uses_kernel_for_ticker_symbols(monkeypatch) -> None: +def test_resolve_action_assets_uses_engine_for_ticker_symbols(monkeypatch) -> None: market = replace( market_config_for_local_offer(), base_asset="HOA", diff --git a/tests/test_offer_bootstrap.py b/tests/test_offer_bootstrap.py index e7664952..ab31e628 100644 --- a/tests/test_offer_bootstrap.py +++ b/tests/test_offer_bootstrap.py @@ -112,7 +112,7 @@ def test_plan_bootstrap_mixed_outputs_rejects_non_string_coin_id() -> None: plan_bootstrap_mixed_outputs(ladder_entries=ladder, spendable_coins=spendable) -def test_plan_bootstrap_mixed_outputs_requires_kernel_symbol(monkeypatch) -> None: +def test_plan_bootstrap_mixed_outputs_requires_engine_symbol(monkeypatch) -> None: import greenfloor.core.engine_bridge as bridge class _Kernel: diff --git a/tests/test_offer_orchestration.py b/tests/test_offer_orchestration.py index 69e59b45..fbf82022 100644 --- a/tests/test_offer_orchestration.py +++ b/tests/test_offer_orchestration.py @@ -6,7 +6,7 @@ from greenfloor.runtime.offer_orchestration import bootstrap_blocks_offer -def test_bootstrap_blocks_offer_uses_kernel_policy_result(monkeypatch) -> None: +def test_bootstrap_blocks_offer_uses_engine_policy_result(monkeypatch) -> None: monkeypatch.setattr( "greenfloor.core.offer_policy.bootstrap_block_error", lambda **_kwargs: "bootstrap_pending:split_submitted", diff --git a/tests/test_offer_publish.py b/tests/test_offer_publish.py index 6d94747f..7b7000bd 100644 --- a/tests/test_offer_publish.py +++ b/tests/test_offer_publish.py @@ -77,7 +77,7 @@ def verify_offer_for_dexie(_offer: str) -> str: ) -def test_verify_offer_for_dexie_reports_missing_kernel(monkeypatch) -> None: +def test_verify_offer_for_dexie_reports_missing_engine(monkeypatch) -> None: monkeypatch.setattr( "greenfloor.core.engine_bridge.import_engine", lambda: (_ for _ in ()).throw(ImportError("greenfloor_engine_unavailable")), @@ -87,7 +87,7 @@ def test_verify_offer_for_dexie_reports_missing_kernel(monkeypatch) -> None: ) -def test_bootstrap_block_error_delegates_to_kernel(monkeypatch) -> None: +def test_bootstrap_block_error_delegates_to_engine(monkeypatch) -> None: class _Native(MinimalSignerEngine): @staticmethod def bootstrap_block_error( @@ -109,7 +109,7 @@ def bootstrap_block_error( ) -def test_bootstrap_block_error_requires_kernel_symbol(monkeypatch) -> None: +def test_bootstrap_block_error_requires_engine_symbol(monkeypatch) -> None: class _Native: pass @@ -122,7 +122,7 @@ class _Native: ) -def test_expected_publish_asset_fields_delegates_to_kernel(monkeypatch) -> None: +def test_expected_publish_asset_fields_delegates_to_engine(monkeypatch) -> None: class _Native(MinimalSignerEngine): @staticmethod def expected_publish_asset_fields( @@ -154,7 +154,7 @@ def expected_publish_asset_fields( } -def test_expected_publish_asset_fields_requires_kernel_symbol(monkeypatch) -> None: +def test_expected_publish_asset_fields_requires_engine_symbol(monkeypatch) -> None: class _Native: pass @@ -215,7 +215,7 @@ def resolve_quote_price_for_pricing(_pricing): assert resolve_quote_price_for_pricing(pricing) == 1.5 -def test_dexie_offer_asset_expectation_error_delegates_to_kernel(monkeypatch) -> None: +def test_dexie_offer_asset_expectation_error_delegates_to_engine(monkeypatch) -> None: class _Native(MinimalSignerEngine): @staticmethod def dexie_offer_asset_expectation_error( @@ -249,7 +249,7 @@ def dexie_offer_asset_expectation_error( ) -def test_dexie_offer_asset_expectation_error_requires_kernel_symbol(monkeypatch) -> None: +def test_dexie_offer_asset_expectation_error_requires_engine_symbol(monkeypatch) -> None: class _Native: pass @@ -265,7 +265,7 @@ class _Native: ) -def test_verify_dexie_offer_visible_by_id_uses_kernel_asset_expectation(monkeypatch) -> None: +def test_verify_dexie_offer_visible_by_id_uses_engine_asset_expectation(monkeypatch) -> None: class _Dexie: @staticmethod def get_offer(_offer_id: str) -> dict[str, object]: diff --git a/tests/test_offer_reconcile_kernel.py b/tests/test_offer_reconcile_engine.py similarity index 100% rename from tests/test_offer_reconcile_kernel.py rename to tests/test_offer_reconcile_engine.py diff --git a/tests/test_retry_policy_parity.py b/tests/test_retry_policy_parity.py index 0977a014..5c57b98c 100644 --- a/tests/test_retry_policy_parity.py +++ b/tests/test_retry_policy_parity.py @@ -30,7 +30,7 @@ def test_coinset_fee_lookup_retry_sleep_parity() -> None: assert coinset_fee_lookup_retry_sleep(2) == 2.0 -def test_call_with_moderate_retry_uses_kernel_backoff() -> None: +def test_call_with_moderate_retry_uses_engine_backoff() -> None: sleeps: list[float] = [] attempts = {"count": 0}