Strategy seams — the substrate map and how to add a strategy

Companion to ADR-054 (frame evidence seams), ADR-055 (pending-transaction strategy seams), ADR-019 (strategy-vs-engine), ADR-025 (execution strategy), ADR-018 (engine-family trigger, pulled by decision at ADR-055 Phase C), and ADR-057 (the strategy host).

The runbook

The step-by-step companion to this map is adding-a-strategy.md: it carries the real signatures, the admission/lane/ledger/hub wiring, the driver partitions, the config-facet additions, and the test-surface pattern.

The shared substrate

Layer

Owner

Notes

Pool state + identity values

degenbot-pools

PoolEntry, family states, slot_layout (the one location table)

Solver math + envelopes

degenbot-solvers

value-only; no chain, no async; SolveOutcome carries the typed Envelope verdict to planning

Path enumeration primitive

degenbot-pathfinding

leaf PathGraph DFS; both discovery styles build on it

Simulation engine

degenbot-simulation

BlockSimHandle, ScratchEvm, replay seam, journal extraction (V2/V3/V4)

Command grammar

degenbot-executor

encode_cmd_stream, EncodeRequest

RPC spine

degenbot-rpc

provider, multicall3, head watch, pending-tx feeds, fee oracle

State owner + admission

degenbot-substrate

live registry BotState (pump-fed) and the planning sandbox (planning::Workspace + ExplicitPoolState) — the substrate’s one home since ADR-067; degenbot-bot composes it as a peer and re-exports it at the historical bot_core::* paths

Session object identity

degenbot-substrate::session_registry

the ONE per-session answer to “is this the same object?” — pools/tokens as typed DashMap + Arc behind get-or-create, paths reached through a PathObjectAdapter handle, positions reached through a PositionObserver handle. Identity only: no live state, no I/O, no solver, no submission. A strategy borrows an object reference and derives its own plan; it never holds a private object map. ADR-064

Pool ingress (Db → Chain tick-map precedence, sealed seed, verify policy)

degenbot-substrate::pool_ingress

the one V3/V4 pool-state admission seam; Db/Chain TickMapSeed provenance is minted only here, and V4 full-map verification targets PoolManager + PoolId rather than StateView

Strategy kit (boot-resolved composition)

degenbot-strategy/src/strategy_kit.rs

StrategyKit::resolve — the provision cell (ingress) + discovery handles a strategy composes

Strategy plane + concrete compositions

degenbot-strategy

the backrun reaction arm (frame pipeline, anchored DFS, gap quarantine, pending-tx driver) and the settlement composition; capability implementations re-exported, never moved; hosted families drive the boot-resolved StrategyKit cells (strategy_kit.rs)

Submission machinery

degenbot-submission

signer, fee/params/bundle, dispatcher, monitor, submission ledger, finality-liveness FSM

The two reaction kinds

A strategy picks exactly ONE:

  • Pending-transaction strategies (PendingTxReaction trait, degenbot-strategy/src/pending_tx.rs) react to observed mempool transactions. The pending-transaction driver owns the substrate loop: simulate the pending tx (ScratchEvm::replay) → recover pool post-states (extract_pool_post_states) → admit → discover → evaluate → compose → bundle-sim gate → decide → submit. BackrunStrategy (degenbot-strategy/src/backrun_strategy.rs) is the reference implementation.

  • Settled-block strategies react to sealed blocks via the block pump / StageMachine seam; settlement arbitrage is the only one today. Their product types are deliberately settlement-shaped; generalization is Phase C work (ADR-055 D5).

Adding a pending-transaction strategy — the checklist

  1. Write one PendingTxReaction impl in degenbot-strategy: your admit selection (from recovered pool post-states), discover (anchored DFS over the connector index, or otherwise), evaluate pricing, compose payload policy, decide gate. State provisioning is a plane capability, not a private one (ADR-061; adding-a-strategy.md §0.1): the boot resolves a StrategyKit once per strategy (StrategyKit::resolve) and hands it in, so a strategy composes kit.provision.ingress / kit.discovery and never an ingress it built itself. Tick maps enter the sandbox only through the sealed TickMapSeed boundary — Db/Chain seeds are minted inside degenbot-substrate::pool_ingress alone, and replay admission is an ingress operation; strategies do not construct a seed or register directly into the workspace. Verification is a typed VerifyLevel policy (default bootstrap) on the provisioning cell; the Tracked intake reconciliation and the two-stamp clock are unconditional integrity, never gated by the knob. Replay, journal extraction, the pathfinding primitive, the sim executor, liveness, and the submission channel are provided.

  2. V4 works out of the box: the journal extracts V4 post-states for pools whose identities your descriptors carry (V4PoolSet), and PoolIngress::admit_v4_replay owns staging, backfill, bitmap merge, verification, and registration. The full-map verifier targets the PoolManager and PoolId; StateView is optional scalar/bootstrap configuration, not a verification target.

  3. Declare your strategy facet in the typed config (each strategy.<name> facet’s active key is its activation — one declaration site).

  4. Choose your SubmissionTarget (Bundle / Public).

  5. Register your launcher/console row so the operator can select the strategy explicitly.

Loud-abort rule (ADR-055 D4): if your strategy asks the substrate for a pool family it cannot serve, that surfaces as a loud typed failure immediately — silent skips are reserved for transient I/O failure, never for capability gaps.

The strategy host (Phase C, landed)

The dynamic strategy host landed 2026-09-19 (ADR-057): StrategyHost (degenbot-bot/src/strategy_host.rs) owns the hub, the boot-snapshot RouteRegistry, and the NonceAuthority (degenbot-bot/src/nonce_authority.rs), and drivers attach through the bound HostHub pair (EngineDriver::from_stages_with_hub, degenbot-bot/src/arb_engine/driver.rs). The per-strategy submission ledger, NonceLane, and the default HeadPolicy live in degenbot-submission/src/submission_ledger.rs; the backrun arm is a registrable driver (degenbot-strategy/src/backrun_driver.rs); and the operator drives admission through the engine adapter’s enable_strategy / disable_strategy / strategies verbs. The driver FSM is Registered → Enabled → Running → {Halted, Disabled} with terminal tombstones, and nonces are leased at sign time (lowest-free, contiguous above the confirmed chain nonce).

Forward-looking statements reserved to the deferred work: shadow-feedback between drivers (reconciliation observes the nonce-level shadow and acts on nothing) and more than one outstanding nonce lease per strategy. Both are post-v1 revisits, as are process-level submission arbitration and lane hot registration.

Adding a settled-block strategy — on-demand engine generalization

The pump/stage seam and its payload typing are settlement-specific today by design (a sample of one). When a second settled-block strategy exists, the ADR-018-named extraction runs: parameterized stage payloads, a generic EngineDriver, per-family fleet globals. The strategy host is landed and can run such a driver; the payload generalization is not pulled by a sample of one, so it stays on-demand.

Phase B landed (2026-09-19): degenbot-eventhub owns per-process intake fan-out with declared overflow policies (OverflowPolicy); the backrun feed ring and head watch subscribe through it (B1/B2), both engine→driver channels are hub-registered UnboundedFlagged sources with observable depth via Hub::named_pending (B3), the gated serving seam was retired (B4, ADR-056), and the boot-snapshot RouteRegistry answers pool membership for strategies (B5). A second pending transaction strategy now implements PendingTxReaction and subscribes; no intake wiring. The Phase C runtime host now lands on top of it (ADR-057).

Where the seams are (exact owners)

Seam

Lives at

Strategy plane (StrategyName, Strategy, SelectedStrategy)

degenbot-strategy/src/strategy_plane.rs

Concrete compositions (MevblockerBackrun, TxpoolBackrun, Settlement)

degenbot-strategy/src/backrun.rs, degenbot-strategy/src/settlement.rs

MarketContext (frame-surviving caches)

degenbot-strategy/src/market_context.rs

PendingTxReaction + artifacts

degenbot-strategy/src/pending_tx.rs

BackrunStrategy (reference composition)

degenbot-strategy/src/backrun_strategy.rs

Pending-tx driver (replay/extract/stages/gate)

degenbot-strategy/src/frame_pipeline.rs

SubmissionTarget + dispatch_and_submit

degenbot-submission/src/submit.rs

Journal extraction (V2/V3/V4)

degenbot-simulation/src/sim/evm/journal_pools.rs

Sandbox + ExplicitPoolState

degenbot-substrate/src/planning.rs

Strategy facets (activation + knobs)

degenbot-config/src/schema.rs (strategy.*)

Strategy host (hub + registry + authority + FSM)

degenbot-bot/src/strategy_host.rs

Nonce authority

degenbot-bot/src/nonce_authority.rs

Hub hoist / driver attach

degenbot-bot/src/arb_engine/driver.rs

Submission ledger + NonceLane + HeadPolicy

degenbot-submission/src/submission_ledger.rs

Registrable backrun driver

degenbot-strategy/src/backrun_driver.rs

Settled-block seam today

degenbot-bot/src/bot_core/stage_handlers.rs