Skip to content

Test Audit Report — Spec-Driven Coverage Phase 0 ​

Historical note (2026-05-20): ADR-017 (EV cap для WATCH+ARM+FAK) удалён полностью. Все упоминания «ADR-017» / EvCap / ev_cap_* / t_ev_cap_* / t_watch_arm_fak_skips_when_limit_above_ev_cap в этой аудите более не соответствуют коду — соответствующие тесты удалены, rule-теги ADR-017 устарели. Документ оставлен как snapshot Phase 0 для исторической трассировки; верифицирован против git history.

Audit existing tests workspace-wide on two dimensions:

  1. Spec-conformance — derived from a business rule (rule-ID), or code-derived tautology?
  2. Liveness — observes a side-effect boundary, or pure-function assertion only?

Source matrices:

  • docs/operations/test-coverage-matrix.md — 172 rule-IDs (G/BA/DIR/MK/MR/MA).
  • docs/operations/feature-liveness-matrix.md — 143 LIV-IDs across 50 feature groups.
  • Existing methodology baseline: docs/operations/liveness-audit-2026-05-06.md (18 ADR + 5 FSD analysis; 16 ranked gaps; recommended TD-31..TD-34).
  • Incident driver: docs/operations/incident-2026-05-06-rtds.md (RTDS silent-broken P1, 4+ days blind).

Methodology applied (verbatim from liveness-audit-2026-05-06.md §1):

ClassDefinitionSufficient?
Spec testAsserts pure function return value over constructed input. No I/O, no executor, no journal, no side-channel observed.NO (alone) — may be code-derived tautology
Liveness testDrives input through production wiring up to a side-effect boundary (OrderClient::place_order, journal row, SSE envelope, log line, metric counter); asserts on what was observed at the boundary.YES
Compile-only#[tokio::test] exercises types but assertions are conditional / guarded; compile passes without runtime check.NO
Static fixture replayE2E replay against frozen Parquet — drives full pipeline. Strong for wiring; weak for external-format drift.YES for wiring, NO for external-contract drift

Five admissible observable boundaries (from existing audit §1):

  1. MockOrderClient.place_calls / cancel_calls / merge_calls / abandon_calls / persist_calls records.
  2. SQLite journal row count via read_conn.query_row(...).
  3. SSE/REST response body via axum_test_helper / reqwest against local_api::serve.
  4. Tracing log line via tracing_subscriber::fmt::TestWriter.
  5. Metrics::snapshot() / MetricsTick projection counter delta.

Test inventory (gathered via grep -rn "^\s*#\[(tokio::)?test\]" on workspace 2026-05-06 from worktree polymarket-engine-tests, branch feat/spec-driven-coverage):

CrateInline #[cfg(test)] modulesInline test fnsIntegration filesIntegration test fnsTotal
pm-bot931197391641361
pm-common6740074
pm-sdk15920092
pm-bot-events0011414
pm-analytics41931231
pm-cli(counted under pm-bot via shared crates)————
Workspace total118138243190~1572

Methodology drift vs liveness-audit-2026-05-06.md headline figure of 1561: rounded to current count from grep on this worktree (1572 distinct #[(tokio::)?test] annotations). Discrepancy ≈ 11 tests added since the audit doc was authored — primarily the post-incident additions in tests/rtds_hook_e2e.rs (3 tests), tests/rtds_active_cancel.rs (4 tests, was previously empty), application/alerts/rule.rs (8 RTDS-health tests). The 1561 figure is preserved as historical reference; 1572 is the working census.

Three buckets ​

Bucket A: liveness-covered — kept ​

Existing tests that already observe a side-effect boundary AND derive from a business rule. Mark with rule-ID / LIV-ID; promote to mapped in matrices.

Boundary column abbreviations: MOC = MockOrderClient.{place,cancel,merge,abandon,persist}_calls; J = SQLite journal row read via read_conn; SSE = reqwest against /api/local/stream; LOG = tracing capture; M = MetricsTick/Metrics::snapshot(); FS = filesystem (capture file bytes); ON = on-chain receipt watcher; PA = panic.fail_fast payload assertion; HTTP = axum_test_helper HTTP status/body; WS = outbound mock WS frame; CLI = assert_cmd exit + stderr.

Test (file:fn)rule-ID(s) coveredLIV-ID(s) coveredBoundary observedNotes
crates/pm-bot/tests/strategy_engine_integration.rs::engine_handle_full_fill_with_autosell_places_sell (line 75)G-3 (dry-run = live pipeline), BA-10 (autosell tick)LIV-11.1MOC.place_callsAsserts side, price, size, order_type via mock.place_calls.lock().unwrap() (tests/strategy_engine_integration.rs:94). Canonical example for ADR-001 / ADR-007 wiring.
crates/pm-bot/tests/strategy_engine_integration.rs::engine_partial_fill_no_autosell_yields_no_io (line 105)BA-10, G-27 (hold-to-resolution)LIV-11.1MOC.place_calls (empty), MOC.cancel_calls (empty)Negative liveness — Decision pipeline runs end-to-end and produces zero side-effects when autosell off.
crates/pm-bot/tests/strategy_engine_integration.rs::engine_periodic_after_timeout_emits_cancels_to_executor (line 127)DIR-13 (periodic scan), G-25LIV-3.1, LIV-11.2MOC.cancel_callsDrives evaluate_periodic through executor; observes Decision::CancelOrder reaching cancel_order.
crates/pm-bot/tests/strategy_engine_integration.rs::engine_market_resolved_redeem_is_skipped_in_minimal_executor (line 162)DIR-21, MK-21, BA-17LIV-12.4 (partial — redeem)MOC.place_calls (empty), MOC.cancel_calls (empty)Compile-only-shape note: minimal executor lacks redeem client — test asserts no place/cancel calls but redeem boundary itself unobserved here. Promote to partial.
crates/pm-bot/tests/strategy_engine_integration.rs::engine_unknown_orderbook_event_is_pure_noop (line 189)G-4 (Decision exhaustive)LIV-11.1MOC (all empty)Negative liveness for unknown event class.
crates/pm-bot/tests/strategy_engine_integration.rs::engine_recover_returns_empty_step45b_stub (line 202)G-25 (recovery)LIV-6.1MOC.list_orders (count)Stub-state assertion — recovery returns empty Vec; does NOT yet load journal — partial coverage of LIV-6.1.
crates/pm-bot/tests/journal_writer.rs::single_event_atomic_commit (line 104)G-26 (fill before insert), ADR-005 INV-ALIV-13.3J + broadcastAsserts row + broadcast envelope strictly post-commit. ADR-005 INV-A canonical pin.
crates/pm-bot/tests/journal_writer.rs::causal_key_idempotent (line 148)G-10 (webhook idempotency cousin), G-31LIV-13.3JIdempotent INSERT OR IGNORE on causal_key; row-count remains 1 after retry.
crates/pm-bot/tests/journal_writer.rs::mpsc_full_drops_without_blocking (line 199)G-26, G-32 (no blocking on hot path)LIV-13.3J + counterDrops envelope when capacity exceeded; counter dropped_total increments.
crates/pm-bot/tests/journal_writer.rs::commit_failure_propagates (line 231)G-26LIV-13.3J + LOGCommit error escalates via tracing; row absent.
crates/pm-bot/tests/journal_writer.rs::broadcast_ordering_under_load (line 299)G-30 (clock monotonic), ADR-005 INV-ALIV-13.3broadcast seqStrict ordering of seq in broadcast tape under N concurrent producers.
crates/pm-bot/tests/journal_writer.rs::rotate_session_atomic_boundary (line 350)G-31, ADR-005 §Session boundaryLIV-13.6J (bot_sessions)Old session closes / new opens transactionally.
crates/pm-bot/tests/journal_writer.rs::shutdown_drains_queue (line 442)ADR-005 INV-ALIV-13.3JShutdown path drains pending mpsc before exit.
crates/pm-bot/tests/journal_writer.rs::snapshot_write_no_event_log_entry (line 485)ADR-005 §snapshotLIV-13.3JSnapshot writes do NOT add event_log row (separate table).
crates/pm-bot/tests/journal_writer.rs::side_effect_basket_state_atomic (line 547)BA-6 (basket atomicity), G-26LIV-13.3J (basket_states)UPDATE+INSERT in single tx.
crates/pm-bot/tests/local_api.rs::health_returns_ok_with_session_id (line 165)ADR-005 §healthLIV-13.1HTTP + bodyGET /health 200 + JSON contains current session_id.
crates/pm-bot/tests/local_api.rs::seed_returns_session_meta_and_last_seq (line 190)ADR-005 §seedLIV-13.1HTTP + bodyGET /seed JSON contains last_seq + session meta.
crates/pm-bot/tests/local_api.rs::stream_replays_from_last_event_id (line 231)ADR-005 §replayLIV-13.5HTTP status onlyCompile-only-shape body: status code asserted but body byte-stream NOT decoded back to EventEnvelope — known gap from existing audit §5. Status: partial, not mapped.
crates/pm-bot/tests/local_api.rs::stream_emits_heartbeat (line 278)ADR-005 §heartbeatLIV-13.4HTTP status onlySame gap class — heartbeat shape not asserted from byte stream. partial.
crates/pm-bot/tests/local_api.rs::rotate_requires_confirm_token (line 303)ADR-013 §C4 paranoid pattern + ADR-005 §rotateLIV-13.6HTTP statusWithout ROTATE token: 400; with: 200.
crates/pm-bot/tests/local_api.rs::bind_rejects_non_loopback_without_flag (line 341)ADR-005 §bind-policy + LIV-2.4 cousinLIV-13.2TCP bind errorBind to non-loopback fails unless flag set.
crates/pm-bot/tests/e2e_webhook_smoke.rs::t_e2e_signal_dry_run_smoke (line 66)G-9 (HMAC), G-11 (allowlist optional)LIV-2.1, LIV-2.2HTTP + receiver mpscReal axum server boots; HMAC-signed POST → 202; deserialised ExternalSignal arrives on mpsc; metrics counter bumps.
crates/pm-bot/tests/scanner_dispatch_test.rs::t_external_signal_reaches_hooks (line 83)G-9LIV-2.6 (partial — to-hook not to-Decision)mpsc receiverConfirms on_external_signal invoked; does NOT assert Decision reaches OrderClient — gap noted in existing audit §3 ADR-003.
crates/pm-bot/tests/scanner_dispatch_test.rs::t_full_channel_maps_to_503 (line 143)ADR-003 §Failure modesLIV-33.1HTTP 503 + countermpsc full → 503 + webhook_channel_full_total += 1.
crates/pm-bot/tests/scanner_dispatch_test.rs::t_disabled_feature_is_vacuous (line 178)ADR-003 §Backward compatLIV-17.3TCP listener absentWEBHOOK_ENABLED=false ⇒ no bind.
crates/pm-bot/tests/rtds_hook_e2e.rs::t_wire_payload_drives_on_rtds_trade_ms (line 278)G-12 (toxic-flow detect via parser path)LIV-1.3, LIV-1.4recorder + mock WSCloses TD-31 LIV-1.4 gap from existing audit. Mock WS server emits canonical ms-magnitude wire payload; recorder captures RtdsEvent::Trade.
crates/pm-bot/tests/rtds_hook_e2e.rs::t_wire_payload_drives_on_rtds_trade_seconds (line 304)G-12 + RCA §4.1LIV-1.3, LIV-1.4recorderSeconds-magnitude payload — exact silent-broken regression guard.
crates/pm-bot/tests/rtds_hook_e2e.rs::t_invalid_timestamp_drops_event (line 331)G-12LIV-1.3M (rtds_events_dropped_*)Drop counter increments for invalid timestamp.
crates/pm-bot/tests/rtds_active_cancel.rs::t_toxic_flow_emits_cancel_when_active (line 157)G-13 (active-cancel gate)LIV-1.7MOC.cancel_calls + reasonCloses TD-32 from existing audit — RTDS_TOXIC_FLOW_ACTIVE_CANCEL_ENABLED=true path emits Cancel Decision through executor.
crates/pm-bot/tests/rtds_active_cancel.rs::t_toxic_flow_negative_log_only_emits_no_decisions (line 237)G-13LIV-1.6LOG + MOC.cancel_calls (empty)LOG_ONLY=true: warn line captured, no cancels.
crates/pm-bot/tests/rtds_active_cancel.rs::t_toxic_flow_negative_default_log_only_emits_no_decisions (line 289)G-13LIV-1.6MOC.cancel_calls (empty)Default-mode safety: no cancels without explicit enable flag.
crates/pm-bot/tests/rtds_active_cancel.rs::t_toxic_flow_isolates_per_condition_id (line 336)G-13LIV-27.2MOC.cancel_calls scopedCross-cid isolation — toxic on cid_A does not cancel orders on cid_B.
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_emits_at_1hz (line 151)G-32 (sonic-rs hot path indirect)ADR-005 §metrics tickMTick interval shape.
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_respects_shutdown (line 183)ADR-005MShutdown drains.
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_carries_emergency_flag (line 216)ADR-005 + AlertRule cousinMEmergency flag propagates.
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_carries_session_id (line 254)ADR-005 §sessionMsession_id projected.
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_omits_rtds_when_unwired (line 299)G-32 cousinLIV-1.8MRTDS counters absent when unwired.
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_projects_rtds_counters_when_wired (line 366)G-12, G-13 indirectLIV-1.8MRTDS counters present when wired — but uses manually seeded counters per existing audit §4 ADR-004; not full parser→hub→tick chain. Status: partial.
crates/pm-bot/tests/event_hub.rs::null_hub_returns_disabled (line 95)ADR-005 §null-modeLIV-13.2EventHub stateNullHub.is_enabled() == false.
crates/pm-bot/tests/event_hub.rs::event_hub_emit_round_trip (line 109)ADR-005 INV-ALIV-13.4 (partial)broadcast ReceiverEnvelope round-trips; full HTTP-SSE path not exercised.
crates/pm-bot/tests/event_hub.rs::dropped_counter_increments_when_full (line 129)ADR-005 backpressureMDrop counter.
crates/pm-bot/tests/event_hub.rs::null_hub_subscribe_does_not_panic (line 163)ADR-005 §null-modeLIV-13.2(no panic)Compile-only-ish; partial.
crates/pm-bot/tests/event_hub.rs::executor_with_null_hub_zero_overhead_smoke (line 180)ADR-005 §null-modeLIV-13.2(no journal calls)Per existing audit §1 — listed as compile-only example. Demote to Bucket B / partial.
crates/pm-bot/tests/event_hub.rs::order_cancel_emits_canonical_event (line 205)ADR-005 INV-ALIV-13.4broadcastCancel envelope shape.
crates/pm-bot/tests/receipt_watcher.rs::receipt_watcher_emits_confirmed_on_success (line 114)MA-11, MR-11 (merge tx confirm)LIV-4.4, LIV-40.5ONMock chain returns Confirmed → emit MergeConfirmed BotEvent.
crates/pm-bot/tests/receipt_watcher.rs::receipt_watcher_drops_on_revert (line 176)MA-11 (Reconcile path)ONRevert → MergeReverted event.
crates/pm-bot/tests/receipt_watcher.rs::receipt_watcher_drops_after_timeout (line 219)MA-10 (Reconcile timeout)ONTimeout → MergeTimeout event.
crates/pm-bot/tests/parallel_placement.rs::parallel_within_group_latency_approx_max_not_sum (line 78)BA-6 (basket atomicity)LIV-12.1(latency budget)Parallelism timing observable.
crates/pm-bot/tests/parallel_placement.rs::rejected_leg_is_isolated_others_still_posted (line 118)BA-6 + LIV-41.2MOC.place_calls subsetINV-20/21 sync cancel branch.
crates/pm-bot/tests/parallel_placement.rs::across_groups_sequential_in_groups_parallel (line 167)BA-6timingParallelism shape across groups.
crates/pm-bot/tests/parallel_placement.rs::neg_risk_preresolved_passes_through_plan (line 208)G-17, BA-1LIV-5.1MOC.place_calls + neg_risk flagneg_risk markets pass through.
crates/pm-bot/tests/abandon_cancels_open_orders.rs::t_apply_cleanup_removes_order_frees_budget_emits_history (line 222)BA-18 (recovery), ADR-013 cancel taxonomyLIV-14.1J + budget readApply cleanup mutation observed in journal.
crates/pm-bot/tests/abandon_cancels_open_orders.rs::t_no_room_cancels_all_posted_orders_of_basket (line 333)BA-6MOC.cancel_calls len == legsCohort-cancel branch.
crates/pm-bot/tests/abandon_cancels_open_orders.rs::t_partial_reject_sync_cancels_posted_legs (line 440)BA-6, INV-20/21LIV-41.2MOC.cancel_callsPartial-reject sync cancel canonical liveness.
crates/pm-bot/tests/abandon_cancels_open_orders.rs::t_sync_cancel_does_not_touch_other_baskets (line 536)BA-6 isolationLIV-41.2MOC.cancel_calls scopedCross-basket isolation.
crates/pm-bot/tests/budget_concurrent_admission.rs::t_serialized_admissions_respect_budget (line 81)G-20, BA-11, DIR-9, MK-17, MR-20, MA-18LIV-16.5 cousinbudget read + MOC.place_callsΣ(allocations) ≤ budget.
crates/pm-bot/tests/budget_concurrent_admission.rs::t_concurrent_admissions_overshoot_budget (line 129)G-20 negative(assertion: caps at budget)Concurrent path stays bounded.
crates/pm-bot/tests/basket_covered_on_full_fill.rs::t_basket_marked_covered_when_all_legs_filled (line 64)BA-6 (atomicity), G-26LIV-41.*JCovered flag flips after final leg fill.
crates/pm-bot/tests/basket_covered_on_full_fill.rs::t_covered_emits_after_repair_when_earlier_attempts_were_rejected (line 164)BA-22 (partial fill recompute)LIV-41.*JRepair semantics.
crates/pm-bot/tests/dirty_basket_retry.rs::t_transient_failure_requeues_basket_and_bumps_count (line 113)BA-6 (resilience) + DIR-25 cousinretry counterTransient retry behaviour.
crates/pm-bot/tests/dirty_basket_retry.rs::t_max_retries_exhausted_moves_to_terminal (line 188)BA-6 backoffterminal stateBackoff caps.
crates/pm-bot/tests/dirty_basket_retry.rs::t_repost_classifier_partial_failure_requeues (line 283)BA-6requeueReclassification on partial failure.
crates/pm-bot/tests/dirty_basket_retry.rs::t_repost_failure_escalates_after_max_retries (line 314)BA-6 backoffescalationEscalation to terminal.
crates/pm-bot/tests/restart_attempt_recovery.rs::t_load_existing_orders_restores_attempt_from_basket_legs (line 48)G-25, BA-18LIV-6.3JRecovery loads attempt counter from journal.
crates/pm-bot/tests/restart_attempt_recovery.rs::t_max_attempt_for_order_picks_highest_across_baskets (line 76)G-25LIV-6.3JCross-basket attempt aggregation.
crates/pm-bot/tests/restart_attempt_recovery.rs::t_restart_with_partially_filled_basket_position_not_double_counted (line 103)G-25, G-26LIV-6.3, LIV-6.4JNo-double-count after restart.
crates/pm-bot/tests/crash_recovery.rs::st15_three_posted_legs_surface_on_startup (line 52)G-25LIV-6.3JCrash recovery surfaces all posted legs.
crates/pm-bot/tests/crash_recovery.rs::st16_cancelled_legs_excluded (line 67)G-25LIV-6.3JCancelled legs excluded from recovery list.
crates/pm-bot/tests/crash_recovery.rs::st17_broken_basket_legs_surface_for_recovery (line 90)G-25LIV-6.3JBroken-basket legs surface.
crates/pm-bot/tests/crash_recovery.rs::st17b_broken_basket_with_rejected_legs_visible_for_drain (line 114)G-25LIV-6.3JDrain semantics.
crates/pm-bot/tests/crash_recovery.rs::st18_processed_trades_hydration (line 134)G-25, G-26LIV-6.3JProcessed trades hydrate from journal.
crates/pm-bot/tests/crash_recovery.rs::st19_order_hash_persists_through_lifecycle (line 160)G-26JOrder hash invariant.
crates/pm-bot/tests/crash_recovery.rs::st20_empty_recovery_is_clean (line 195)G-25JEmpty-DB recovery yields empty state.
crates/pm-bot/tests/crash_recovery.rs::st21_multi_basket_recovery_ordering (line 206)G-30JMulti-basket ordering preserved.
crates/pm-bot/tests/crash_recovery.rs::st22_covered_basket_still_surfaces_for_review (line 225)G-25JCovered baskets visible for review.
crates/pm-bot/tests/crash_recovery.rs::t15_partial_fill_hydration_uses_remaining_not_original (line 258)G-26JHydration uses remaining size.
crates/pm-bot/tests/crash_recovery.rs::t15_unfilled_order_hydrates_at_full_original_size (line 286)G-25JUnfilled hydrates full size.
crates/pm-bot/tests/crash_recovery.rs::t15_fully_matched_live_order_clamps_to_zero (line 303)G-25JFully-matched clamps.
crates/pm-bot/tests/manual_cancel_attempt.rs::t_cancel_order_uses_order_basket_leg_attempt_after_partial (line 77)BA-22J + MOC.cancel_callsAttempt-counter post-partial.
crates/pm-bot/tests/manual_cancel_attempt.rs::t_cancel_order_writes_attempt_1_when_no_prior_partial (line 173)BA-22JAttempt=1 default.
crates/pm-bot/tests/expiry_sync_attempt.rs::t_sync_order_status_expired_uses_order_basket_leg_attempt (line 71)G-25, BA-22JExpiry sync uses leg attempt.
crates/pm-bot/tests/partial_fill_history.rs::t_repeated_partial_fills_create_one_open_position_history_row (line 74)G-26, BA-22JOne-row invariant for partial-fill stream.
crates/pm-bot/tests/partial_fill_history.rs::t_partial_fill_keeps_order_status_partial_not_filled (line 154)G-26JStatus transition correctness.
crates/pm-bot/tests/partial_fill_history.rs::t_position_close_after_multi_partial_computes_correct_pnl (line 187)G-26, ADR-014 PnLJ + computed valuePnL correctness over partial stream.
crates/pm-bot/tests/ws_fill_before_insert_race.rs::t_control_insert_before_fill_creates_position (line 205)G-26JBaseline (control) for race.
crates/pm-bot/tests/ws_fill_before_insert_race.rs::t_race_fill_before_insert_eventually_creates_position (line 254)G-26J + retryRace-tolerance — out-of-order fill+insert eventually consistent.
crates/pm-bot/tests/ws_fill_before_insert_race.rs::t_rest_replay_after_ws_drop_creates_position (line 314)G-26, G-25JREST replay after WS drop.
crates/pm-bot/tests/sync_order_status_rest_path.rs::t_rest_matched_decision_full_routes_via_handle_fill_with_order (line 121)G-26LIV-12.3J + handler invocationREST-path routes via fill handler.
crates/pm-bot/tests/sync_order_status_rest_path.rs::t_skip_ambiguous_rest_reinserts_order_and_preserves_reservation (line 290)G-26, G-20JReservation invariant under ambiguous REST sync.
crates/pm-bot/tests/sync_order_status_rest_path.rs::t_skip_ambiguous_rest_preserves_future_ws_fill (line 331)G-26J + future-event tapeFuture WS fill not dropped after ambiguous REST.
crates/pm-bot/tests/watch_arm_fak_recovery.rs::t_main_recovery_loads_terminal_blacklist_from_journal (line 133)G-25, ADR-017 (proposed)LIV-6.3JTerminal blacklist loaded; recovery does not retry.
crates/pm-bot/tests/preflight_best_bid_ask.rs::denver_regression_bid_ascending_returns_highest_bid (line 58)BA-21 (best_ask correctness), ADR-013(bid/ask returned)Spec test (pure) but real-world fixture (denver regression) — derives from production bug. Borderline: Bucket A on rule-binding even though pure.
crates/pm-bot/tests/preflight_best_bid_ask.rs::cape_town_regression_low_price_tick_thousandth (line 79)BA-21(price-tick)Real-world regression. Bucket A.
crates/pm-bot/tests/preflight_best_bid_ask.rs::order_independence_across_all_permutations (line 105)BA-21 (proptest-shape)(permutation invariant)Proptest-shape over real fixture.
crates/pm-bot/tests/preflight_best_bid_ask.rs::e2e_denver_live_price_via_validate_basket (line 152)BA-21, BA-6(basket validate path)Full validate path.
crates/pm-bot/tests/preflight_best_bid_ask.rs::ws_book_best_bid_returns_max_bid (line 238)BA-21 cousin (WS path)(WS book)Mirrors REST regression on WS path.
crates/pm-bot/tests/event_fsm_transitions.rs::t_register_candidate_on_new_market (line 108)BA-1, BA-6LIV-12.2FSM stateNew market registers as Candidate.
crates/pm-bot/tests/event_fsm_transitions.rs::t_register_multiple_events (line 122)BA-2LIV-12.2FSM countMulti-event registration.
crates/pm-bot/tests/event_fsm_transitions.rs::t_candidate_to_tracking (line 139)BA-13LIV-12.2FSM transitionCandidate→Tracking on book update.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_wait_stale_book (line 151)BA-13, G-28(Decision::Hold)Stale book holds.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_wait_sum_too_high (line 191)BA-4(Decision::Hold)Σask above floor holds.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_wait_profit_too_low (line 222)BA-4, BA-21(Decision::Hold)Profit floor enforced.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_wait_budget_exceeded (line 258)G-20, BA-11(Decision::Hold)Budget cap.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_wait_dedup (line 286)LIV-7.2 cousin(Decision::Hold)Dedup of in-flight.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_ready_all_conditions_met (line 316)BA-3, BA-4, BA-22(Decision::PostBucketArbBasket)Ready emits PostBasket.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_abandon_when_accepting_orders_false (line 414)G-22, BA-2(Decision::Cancel/Abandon)accepting=false → abandon.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_abandon_market_closed (line 457)BA-12abandonMarket-closed abandon.
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_abandon_when_market_becomes_inactive (line 504)G-22abandonInactive abandon.
crates/pm-bot/tests/event_fsm_transitions.rs::t_transition_to_participating_flips_phase (line 555)BA-6FSM phasePhase flip.
crates/pm-bot/tests/replay_determinism.rs::t_replay_ten_runs_yield_identical_hash (line 30)G-14, G-30LIV-8.3, LIV-31.1SHA-256 stabilityCanonical replay-parity gate.
crates/pm-bot/tests/replay_determinism.rs::t_all_frozen_v1_fixtures_yield_consistent_hash (line 78)G-14LIV-8.3SHA-256 stabilityAll fixtures stable.
crates/pm-bot/tests/replay_determinism_smoke.rs::t_replay_determinism_10x_same_session_strategy_yields_same_digest (line 127)G-14LIV-50.1SHA-256Smoke version.
crates/pm-bot/tests/replay_determinism_smoke.rs::t_replay_independence_different_strategy_same_session_does_not_alias (line 167)G-14LIV-50.1SHA-256Independence assertion.
crates/pm-bot/tests/replay_determinism_smoke.rs::t_replay_different_capture_yields_different_digest (line 200)G-14 negativeLIV-50.1SHA-256 differsNegative replay parity.
crates/pm-bot/tests/capture_smoke.rs::t_jsonl_zstd_roundtrip_preserves_frames (line 76)G-15LIV-8.1FSCapture file round-trips.
crates/pm-bot/tests/capture_smoke.rs::t_parquet_frozen_v1_schema_matches_jsonl (line 122)G-15LIV-8.1FS schemaSchema parity.
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_1_jsonl_roundtrip_preserves_frames (line 150)G-15LIV-8.1FSJSONL gate.
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_2_golden_hash_stable_across_ten_runs (line 197)G-14LIV-8.3SHA-256Golden gate.
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_2b_all_fixtures_yield_consistent_hash (line 217)G-14LIV-8.3SHA-256All-fixtures gate.
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_3_parquet_schema_matches_current (line 247)G-15LIV-8.1FS schemaParquet gate.
crates/pm-bot/tests/e2e_bucket_arb_replay.rs::t_bucket_arb_full_cyyz_3day_replay (line 126)BA-25 (10×), BA-1..BA-22 indirectLIV-8.3 + bucket-arb suitefull pipelineStatic fixture replay — strong wiring liveness, weak external-format drift (per methodology).
crates/pm-bot/tests/e2e_directional_replay.rs::t_directional_yes_profile_cyyz (line 149)DIR-2, DIR-19, DIR-32LIV-8.3full pipelineYes profile equivalence.
crates/pm-bot/tests/e2e_directional_replay.rs::t_directional_no_profile_cyyz (line 172)DIR-3, DIR-19LIV-8.3full pipelineNo profile.
crates/pm-bot/tests/e2e_directional_replay.rs::t_directional_harvester_profile_cyyz (line 192)DIR-12, DIR-13, DIR-19LIV-3.*full pipelineHarvester profile.
crates/pm-bot/tests/e2e_directional_replay.rs::t_directional_quickflip_profile_cyyz (line 217)DIR-18, DIR-19LIV-8.3full pipelineQuickFlip profile.
crates/pm-bot/tests/e2e_maker_rebate_replay.rs::t_maker_rebate_full_cyyz_replay (line 106)MR-25 (10×), MR-1..MR-28 indirectLIV-4.*full pipelineMaker-rebate replay.
crates/pm-bot/tests/e2e_maker_replay.rs::t_maker_no_maker_profile_cyyz (line 163)MK-15, MK-16LIV-8.3full pipelineNoMaker profile.
crates/pm-bot/tests/e2e_maker_replay.rs::t_maker_spread_maker_profile_cyyz (line 183)MK-14, MK-16LIV-8.3full pipelineSpreadMaker profile.
crates/pm-bot/tests/e2e_maker_replay.rs::t_maker_sniper_profile_cyyz (line 206)MK-13, MK-16LIV-8.3full pipelineSniper profile.
crates/pm-bot/tests/session_lifecycle.rs::start_creates_session_row (line 56)G-31, ADR-005 §sessionLIV-13.1J (bot_sessions)Session row inserted.
crates/pm-bot/tests/session_lifecycle.rs::rotate_closes_old_opens_new (line 82)ADR-005 §rotateLIV-13.6JRotate atomicity.
crates/pm-bot/tests/session_lifecycle.rs::crash_recovery_marks_stale_session_ended (line 103)G-25, ADR-005JStale session end.
crates/pm-bot/tests/session_lifecycle.rs::concurrent_instance_fails_fast (line 137)ADR-005 INV (single-active)PASecond instance fails fast.
crates/pm-bot/tests/session_lifecycle.rs::heartbeat_updates_last_heartbeat_at (line 165)ADR-005 §heartbeatJHeartbeat updates row.
crates/pm-bot/tests/session_lifecycle.rs::redact_env_for_hash_excludes_secrets (line 214)G-9, G-31(hash invariant)Secrets stripped from session-id hash.
crates/pm-bot/tests/session_lifecycle.rs::redact_env_for_hash_is_deterministic_under_key_reordering (line 241)G-30 cousin(hash deterministic)Determinism of session hash.
crates/pm-bot/tests/journal_migrations.rs::fresh_db_installs_all_tables (line 97)G-31 (schema version)JSchema install.
crates/pm-bot/tests/journal_migrations.rs::idempotent_double_run (line 163)G-31JIdempotent migration.
crates/pm-bot/tests/journal_migrations.rs::rejects_legacy_ladder_db (line 174)G-31PALegacy schema rejected.
crates/pm-bot/tests/journal_migrations.rs::bot_sessions_mode_defaults_to_live (line 197)ADR-005 + ADR-009 §bot_sessions.modeJDefault mode='live'.
crates/pm-bot/tests/journal_migrations.rs::uq_active_session_enforces_one_active (line 221)ADR-005 single-activeJ constraintUnique-constraint enforces one active.
crates/pm-bot/tests/journal_migrations.rs::uq_event_causal_key_idempotency (line 259)G-26J constraintIdempotency via UNIQUE on causal_key.
crates/pm-bot/tests/journal_migrations.rs::wal_mode_set (line 305)G-26(PRAGMA journal_mode=WAL)WAL configured.
crates/pm-bot/tests/journal_migrations.rs::db_and_journal_migrate_converge (line 320)G-31JConvergence of two migrators.
crates/pm-bot/tests/flush_ack_ordering.rs::shutdown_acks_post_commit (line 32)ADR-005 INV-AJ + ackAck order respects commit.
crates/pm-bot/tests/ws_load_test.rs::test_ws_orderbook_load (line 547)(load shape)(rate)Load test — boundary is throughput, not correctness. Borderline; keep here.
crates/pm-bot/tests/ws_load_test.rs::test_event_loop_metrics (line 910)(counter)MMetric counter under load.
crates/pm-bot/tests/ws_load_test.rs::test_mock_server_sanity (line 1163)(mock health)(mock-shape)Sanity.
crates/pm-bot/tests/ws_measure.rs::measure_ws_event_rates (line 194)(measurement)(rate)Operational benchmark — Bucket A by virtue of full WS pipeline driving.
crates/pm-bot/src/application/executor.rs::t_execute_place_ok_returns_order_id (line 2081)G-3, BA-7, MK-11, MR-18LIV-11.1MOC.place_callsInline-module liveness — uses MockOrderClient.
crates/pm-bot/src/application/executor.rs::t_execute_cancel_ok (line 2099)G-21, BA-9, MK-18, MR-27, MA-17LIV-11.2MOC.cancel_callsCancel reaches mock.
crates/pm-bot/src/application/executor.rs::t_execute_place_failure_returns_failed_continues_batch (line 2113)G-3 (resilience)MOC.place_calls + statusBatch resilience.
crates/pm-bot/src/application/executor.rs::t_execute_gtd_default_horizon_applied (line 2135)DIR-25 cousinMOC.place_calls + GTDGTD horizon.
crates/pm-bot/src/application/executor.rs::t_execute_empty_batch_returns_empty (line 2148)G-4MOC (empty)Empty batch.
crates/pm-bot/src/application/executor.rs::t_execute_hold_is_ok_noop (line 2155)G-27, DIR-27MOC (empty)Hold no-op.
crates/pm-bot/src/application/executor.rs::t_redeem_standard_ok_forwards_tx_hash (line 2167)G-19, BA-17, DIR-21, MK-21LIV-43.1redeem-mockStandard redeem.
crates/pm-bot/src/application/executor.rs::t_redeem_neg_risk_ok_with_both_tokens (line 2191)G-18, G-19LIV-43.2NegRiskAdapter mockneg_risk redeem path.
crates/pm-bot/src/application/executor.rs::t_redeem_neg_risk_missing_tokens_skipped (line 2213)G-18(skip path)Defensive skip.
crates/pm-bot/src/application/executor.rs::t_redeem_without_client_skipped (line 2238)G-19(skip path)No-client skip.
crates/pm-bot/src/application/executor.rs::t_redeem_client_failure_returns_failed (line 2258)G-19 (resilience)statusClient failure surfaced.
crates/pm-bot/src/application/executor.rs::t_abandon_ok_invokes_client (line 2282)BA-18MOC.abandon_callsAbandon reaches client.
crates/pm-bot/src/application/executor.rs::t_modify_ok_cancels_then_places_and_returns_new_order_id (line 2324)BA-9, MK-18, MR-27MOC.cancel + placeModify = cancel+place.
crates/pm-bot/src/application/executor.rs::t_modify_cancel_fails_does_not_place (line 2354)BA-9 negativeMOC.place (empty)Cancel-failure short-circuits.
crates/pm-bot/src/application/executor.rs::t_modify_place_fails_after_cancel_ok_returns_diagnostic (line 2380)BA-9 (resilience)status + diagnosticDiagnostic on partial failure.
crates/pm-bot/src/application/executor.rs::t_persist_with_client_writes_through (line 2432)G-26MOC.persistWrite-through observable.
crates/pm-bot/src/application/executor.rs::t_persist_with_client_failure_surfaces_failed (line 2463)G-26statusFailure surfaced.
crates/pm-bot/src/application/executor.rs::t_execute_post_basket_complete_returns_ok (line 2567)BA-6MOCPostBasket ok.
crates/pm-bot/src/application/executor.rs::t_execute_post_basket_partial_returns_partial (line 2602)BA-6MOC + partial statusPostBasket partial.
crates/pm-bot/src/application/executor.rs::t_execute_post_basket_all_failed_returns_failed (line 2637)BA-6MOC + all-failedPostBasket failure.
crates/pm-bot/src/application/executor.rs::t_dryrun_no_live_clob_calls (line 2673)G-3MOC (empty live)Dry-run isolation.
crates/pm-bot/src/application/engine.rs:557..659 (5 anonymous tests)G-3, G-4LIV-11.*MOCFive inline engine tests, each asserting mock.place_calls/cancel_calls.
crates/pm-bot/src/io/clob/dry_run.rs::t_sim_fill_publishes_event_to_dispatcher (line 954)G-3(event publish)Dry-run sim publishes fill.
crates/pm-bot/src/io/clob/dry_run.rs::t_maker_buy_fills_when_best_ask_crosses_bid (line 1015)G-16 (post_only invariant negative — taker scenario detection)(fill state)Maker-buy fill semantics.
crates/pm-bot/src/io/clob/dry_run.rs::t_maker_buy_no_fill_when_best_ask_above_bid (line 1047)G-16(no fill)Maker-buy no-cross.
crates/pm-bot/src/io/clob/dry_run.rs::t_maker_sell_fills_when_best_bid_crosses_ask (line 1080)G-16(fill state)Maker-sell fill.
crates/pm-bot/src/io/clob/dry_run.rs::t_taker_cumulative_fill_unchanged (line 1119)G-16(cumulative invariant)Taker fill cumulative.
crates/pm-bot/src/io/clob/dry_run.rs::t_sim_fill_set_dispatcher_idempotent (line 1152)G-3(idempotency)Dispatcher set is idempotent.
crates/pm-bot/src/io/clob/simulator/mock_clob.rs::t_match_trade_fills_bot_bid_on_sell (line 542)G-16(bot-fill state)Matching on Sell side.
crates/pm-bot/src/io/clob/simulator/mock_clob.rs::t_match_trade_no_fill_when_price_too_low (line 574)G-16(no fill)Price-too-low rejects.
crates/pm-bot/src/io/clob/simulator/mock_clob.rs::t_match_trade_partial_fill_caps_to_trade_size (line 635)G-26(cap invariant)Partial caps.
crates/pm-bot/src/io/clob/simulator/mock_clob.rs::t_match_trade_splits_across_orders_deterministically (line 660)G-30(deterministic split)Deterministic ordering.
crates/pm-bot/src/io/clob/simulator/mock_clob.rs::t_match_trade_skips_cancelled_orders (line 697)G-21(skip invariant)Cancelled orders excluded.
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_fires_on_dropped_parse (line 754)G-12 indirect, RCA §6.2LIV-1.9AlertRule emitCloses TD-33 partially — rule fires on dropped_parse > 0.
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_fires_on_dropped_stale (line 781)RCA §4.1LIV-1.9AlertRuleFires on stale-drop.
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_fires_on_reconnect_storm (line 794)ADR-004 §reconnectLIV-1.9, LIV-32.2AlertRuleFires on reconnect storm.
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_fires_on_stuck_subscription (line 820)ADR-004 freshnessLIV-1.9AlertRuleFires on stuck subscription.
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_hysteresis (line 854)ADR-004LIV-1.9AlertRuleHysteresis prevents flap.
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_no_fire_when_healthy (line 873)ADR-004LIV-1.9AlertRule (silent)Healthy → no fire.
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_no_fire_when_metrics_absent (line 898)ADR-004LIV-1.9AlertRule (silent)Absent metrics → no fire (no spurious).
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_zero_stuck_threshold_disables_stuck_branch (line 921)ADR-004LIV-1.9AlertRuleThreshold=0 disables branch.
crates/pm-bot/src/application/scanner.rs::t_bucket_arb_resolution_yields_neg_risk_redeem_info (line 2315)G-18, G-19, BA-17LIV-12.4LIV-43.2Resolution yields neg_risk redeem context.
crates/pm-bot/src/application/scanner.rs::t_bucket_arb_resolution_idempotent (line 2361)G-19(idempotency)Idempotent resolution.
crates/pm-bot/src/application/scanner.rs::t_collect_filters_bucket_arb_returns_event_slugs (line 2487)RTDS subscribe filterLIV-1.2(filter set)Collects event-slugs.
crates/pm-bot/src/application/scanner.rs::t_collect_filters_directional_returns_market_slugs (line 2519)RTDS subscribeLIV-1.2(filter set)Collects market-slugs.
crates/pm-bot/src/io/webhook/server.rs::t_returns_202_on_valid_signal (line 498)G-9LIV-2.2HTTPValid HMAC → 202.
crates/pm-bot/src/io/webhook/server.rs::t_returns_401_bad_sig (line 512)G-9 negativeLIV-2.2HTTPBad sig → 401.
crates/pm-bot/src/io/webhook/server.rs::t_returns_415_non_json (line 528)ADR-003 §content-type(HTTP 415)Non-JSON.
crates/pm-bot/src/io/webhook/server.rs::t_returns_429_when_burst_exhausted (line 555)ADR-003 §rate-limitLIV-35.1HTTP 429Burst exhausted.
crates/pm-bot/src/io/webhook/server.rs::t_returns_409_on_duplicate (line 580)G-10LIV-7.1HTTP 409Duplicate dedup.
crates/pm-bot/src/io/webhook/server.rs::t_returns_410_on_expired (line 603)ADR-003 §expiry(HTTP 410)Expired signal.
crates/pm-bot/src/io/webhook/server.rs::t_returns_401_on_ip_not_allowed (line 630)G-11LIV-2.4HTTP 401IP allowlist deny.
crates/pm-bot/src/io/webhook/server.rs::t_health_ok_unauthenticated (line 647)ADR-003(HTTP 200)Health unauthenticated.
crates/pm-bot/src/io/webhook/server.rs::t_returns_503_on_channel_full (line 668)ADR-003 §back-pressureLIV-33.1HTTP 503Channel-full → 503.
crates/pm-bot/src/io/webhook/server.rs::t_returns_400_on_idempotency_mismatch (line 684)G-10 negativeLIV-7.1HTTP 400Idempotency mismatch.
crates/pm-bot/src/io/webhook/server.rs::t_rate_limiter_allows_burst_then_blocks (line 710)ADR-003LIV-35.1(limiter state)Burst-then-block sequence.
crates/pm-bot/src/io/webhook/server.rs::t_rate_limiter_refills_over_time (line 723)ADR-003LIV-35.1(limiter state)Refill semantics.
crates/pm-bot/src/io/webhook/server.rs::t_logs_never_contain_secret (line 745)G-9LOG (negative)Secret never in logs.
crates/pm-bot/src/io/local_api/auth.rs::t_correct_token_passes (line 95)ADR-005 §auth(auth-pass)Auth pass.
crates/pm-bot/src/io/local_api/auth.rs::t_missing_header_fails (line 101)ADR-005(auth-fail)Missing header.
crates/pm-bot/src/io/local_api/auth.rs::t_wrong_token_fails (line 119)ADR-005(auth-fail)Wrong token.
crates/pm-bot/src/io/local_api/mod.rs::t_bind_accepts_loopback_v4 (line 369)ADR-005 §bindLIV-13.1TCPLoopback v4 bind.
crates/pm-bot/src/io/local_api/mod.rs::t_bind_accepts_loopback_v6 (line 376)ADR-005 §bindLIV-13.1TCPLoopback v6 bind.
crates/pm-bot/src/io/local_api/mod.rs::t_bind_rejects_non_loopback_without_flag (line 395)ADR-005 §bind-policyLIV-13.1TCP refuseNon-loopback rejected.
crates/pm-bot/src/io/local_api/mod.rs::t_bind_allows_non_loopback_with_flag (line 405)ADR-005 §bind-policyLIV-13.1TCPNon-loopback allowed with flag.
crates/pm-bot/src/persistence/wallets.rs::test_add_and_unlock_roundtrip (line 748)ADR-015 §D1 (AES-GCM round-trip)LIV-16.4(decrypt success)Round-trip plaintext→encrypted→plaintext.
crates/pm-bot/src/persistence/wallets.rs::test_rotate_master_re_encrypts_all (line 823)ADR-015 + LIV-26.1LIV-26.1(re-encrypt invariant)Rotate-master closes LIV-26.1.
crates/pm-bot/src/persistence/wallets.rs::test_rotate_master_rejects_wrong_old_passphrase (line 861)ADR-015(auth fail)Wrong old passphrase rejected.
crates/pm-bot/src/persistence/wallets.rs::test_wrong_master_password_fails_decrypt (line 917)ADR-015(auth fail)AES-GCM auth-tag failure.
crates/pm-bot/src/persistence/wallets.rs::test_rotate_master_atomic_on_partial_failure (line 1212)ADR-015(atomic)Atomicity of rotate.
crates/pm-bot/src/persistence/strategy_config.rs::test_budget_predicate_accepts_within_balance (line 1077)ADR-015 FE-5, G-20LIV-16.5(predicate true)Budget predicate at provisioning.
crates/pm-bot/src/persistence/strategy_config.rs::test_budget_predicate_rejects_excess (line 1090)ADR-015 FE-5LIV-16.5(predicate false)Excess rejected.
crates/pm-bot/src/persistence/strategy_config.rs::test_budget_predicate_disabled_strategies_excluded (line 1105)ADR-015 FE-5LIV-16.5(predicate scope)Disabled excluded.
crates/pm-bot/src/persistence/strategy_config.rs::test_budget_predicate_isolates_per_wallet (line 1158)ADR-015 INV-3 cousinLIV-16.5(predicate scope)Per-wallet isolation.
crates/pm-bot/src/io/rtds/parse.rs::t_parse_trade_event_happy_path (line 473)G-12LIV-1.3(parsed event)Happy path parse.
crates/pm-bot/src/io/rtds/parse.rs::t_rejects_stale_event_by_age (line 544)RCA §4.1LIV-1.3(rejection)Stale-event rejection — guards exact RCA path.
crates/pm-bot/src/io/rtds/parse.rs::t_parse_timestamp_ms (line 563)G-12, RCA §4.1LIV-1.3(parsed ts)Q3 timestamp invariant. Author docstring documented the precise failure mode that broke RCA — see RCA §5.3. Marked Bucket A but with caveat that it does NOT validate the seconds-magnitude direction; that's covered by tests/rtds_hook_e2e.rs::t_wire_payload_drives_on_rtds_trade_seconds.
crates/pm-bot/src/io/rtds/parse.rs::t_parse_orders_matched_same_schema (line 494)ADR-004 Q2LIV-46.1 (partial)(parsed event)Schema parity (orders_matched).
crates/pm-bot/src/io/rtds/dedupe.rs::t_dedupe_first_tx_new ... t_dedupe_thread_safe (lines 167..247, 8 tests)ADR-004 §D7LIV-36.1(dedup state)RTDS tx-hash dedup behaviour.
crates/pm-bot/src/io/rtds/subscribe.rs::t_build_subscribe_event_slug (line 354)G-12 (subscribe wire)LIV-1.2(frame shape)Subscribe frame shape.
crates/pm-bot/src/io/rtds/subscribe.rs::t_filter_set_diff_adds_new ... t_filter_set_diff_no_change_returns_empty (lines 431..461, 3 tests)LIV-49.1LIV-49.1(diff invariant)Diff-resubscribe invariant.
crates/pm-bot/src/io/webhook/dedupe.rs::t_dedupe_returns_duplicate_on_second_call (line 149)G-10LIV-7.1(dedup state)Dedup core.
crates/pm-bot/src/io/webhook/dedupe.rs::t_dedupe_evicts_after_ttl (line 172)G-10LIV-7.1(TTL eviction)TTL bounded.
crates/pm-bot/src/io/webhook/dedupe.rs::t_dedupe_capacity_enforced (line 187)G-10LIV-7.1(capacity bound)LRU bounded.
crates/pm-bot/src/io/webhook/auth.rs::t_hmac_accepts_valid ... t_hmac_rejects_wrong_secret (lines 96..150, 6 tests)G-9LIV-2.2(HMAC accept/reject)HMAC accept/reject.
crates/pm-bot/src/timezone/mod.rs (16 tests)G-1, ADR-016LIV-9.1, LIV-9.2(deadline value)TZ cache — 13 of 16 listed by existing audit as full ADR-016 coverage; gap is consumer-side wiring (LIV-9.3) covered in Bucket C.
crates/pm-bot/src/persistence/journal/mod.rs::t_run_migrations_installs_capture_sessions_table (line 453)ADR-009 §session modelLIV-18.2Jcapture_sessions schema install.
crates/pm-bot/src/persistence/journal/mod.rs::t_capture_sessions_status_check_constraint (line 492)ADR-009J constraintStatus constraint.
crates/pm-bot/src/persistence/journal/reader.rs::t_journal_reader_recent_terminal_watch_arm_fak_cids_returns_done_states (line 333)ADR-017LIV-6.3JReader returns terminals.
crates/pm-bot/src/persistence/journal/reader.rs::t_journal_reader_recent_active_watch_arm_fak_states_returns_latest_per_cid (line 419)ADR-017LIV-6.3JLatest-per-cid.
crates/pm-analytics/tests/replay_smoke.rs::* (5 tests)G-14LIV-8.3full pipelinepm-analytics replay smoke.
crates/pm-analytics/tests/whatif_integration.rs::* (3 tests)ADR-009 §Phase 6LIV-19.3CLIWhat-if env-override via CLI.
crates/pm-analytics/tests/sweep_integration.rs::* (4 tests)ADR-009 §Phase 5LIV-19.1CLI + filesystemSweep parallelism.
crates/pm-bot-events/tests/wire_format.rs::* (14 tests)ADR-005 §wireLIV-13.4 (partial)(wire bytes)Wire-format pin — but compile-time-only on field shape; SSE byte-stream not exercised. Borderline, place in Bucket A on rule-binding even though spec-shape only.

Bucket A subtotal: ~210 tests (138 inline + 72 integration). Promote to mapped in matrices.

Bucket B: code-derived tautology — mark for rewrite ​

Tests asserting on pure function output without link to a business rule. May still pass mutation testing if the assertion is sharp, but do not prove spec-conformance. Recommend rewrite as L1 spec-derived (rule-ID-bound) or delete if redundant.

The vast majority of inline tests in pm-bot/src/domain/strategies/{directional,bucket_arb,maker,maker_rebate,merge_arb}.rs and in pm-bot/src/application/order_manager.rs fall in this bucket: they assert that the pure evaluator returned a particular value over a constructed input, but do NOT drive that evaluator's output through the executor / journal / SSE / metric / log boundary. That's the canonical RTDS pattern (per liveness-audit-2026-05-06.md §1) — pure unit tests stay green when wiring is silently broken.

For brevity, this section catalogues representative families with file ranges and test-fn glob, plus the suggested rule-ID to bind. Each entry covers ≥ 8 tests in that range.

Test (file:fn glob)Why tautologySuggested rule-ID to bindRewrite priority
crates/pm-bot/src/domain/strategies/directional.rs::t_sweep_* (lines 4845..4892, 5 tests)Pure compute_sweep walks asks; asserts output Decimal. No executor / journal observation.DIR-6, DIR-10, BA-21LOW — sweep math correctness, but rewrite as pair (compute_sweep, place_call) once mapped to LIV-3.3 / LIV-11.1
crates/pm-bot/src/domain/strategies/directional.rs::t_profile_*_equivalent_* (lines 4892..4999, 4 tests)Asserts profile produces expected Decision shape — pure.DIR-19 (profile equivalence)MEDIUM — should be paired with replay-parity proof; current tests/e2e_directional_replay.rs covers the SHA-side, but per-profile MOC.place_calls is missing
crates/pm-bot/src/domain/strategies/directional.rs::t_skip_when_* (lines 5071..5145, 5 tests)Skip-reason returns: pure decision-tree assertion.DIR-5, DIR-6, DIR-10LOW — math is fine; bind to LIV-30.2 (negative scope)
crates/pm-bot/src/domain/strategies/directional.rs::t_strategy_construction / t_clone_shares_position_map (lines 5145..5161)Construction-time invariants. Code-derived.(none — internal invariant)DELETE candidate if not asserting public contract
crates/pm-bot/src/domain/strategies/directional.rs::t_on_full_fill_* (lines 5161..5235, 4 tests)Asserts FSM mutation on fill — but on a synthetic StrategyState, not driven through journal.G-26, DIR-30MEDIUM — rewrite as journal-observed test (LIV-12.3)
crates/pm-bot/src/domain/strategies/directional.rs::t_on_market_resolved_* (lines 5235..5288, 2 tests)Resolved-event mutation; pure.DIR-21MEDIUM — rewrite to assert MOC.redeem_calls (LIV-12.4)
crates/pm-bot/src/domain/strategies/directional.rs::t_late_entry_* (lines 5391..5500, 3 tests)Late-entry gate as pure decision.DIR-8, ADR-016 §D5MEDIUM — rewrite as LIV-9.3 (TZ used at evaluate_signal)
crates/pm-bot/src/domain/strategies/directional.rs::t_max_hours_to_expiry_* (lines 5586..5654, 5 tests)DEAD-CODE check per memory max_hours_dead_code — asserts that field doesn't filter, only late_entry does. Code-derived (asserts implementation, not spec).DIR-7 (which is itself documented as gap)DELETE candidate — these enshrine dead code; either delete and remove field, or rewrite as proof field is unused via cargo modules.
crates/pm-bot/src/domain/strategies/directional.rs::t_post_only_entry_sets_post_only_true (line 5776)Asserts struct field. Code-derived, asserts compile-time invariant at runtime.G-16LOW — bind to G-16; or delete as compile-time enforced
crates/pm-bot/src/domain/strategies/directional.rs::t_entry_price_min_filters_below_floor (line 5807)Pure filter — same family as DIR-5/6.DIR-6LOW
crates/pm-bot/src/domain/strategies/directional.rs::t_use_orderbook_sweep_runs_regardless_of_entry_mode (line 5830)Pure flag-routing.(internal config)DELETE candidate
crates/pm-bot/src/domain/strategies/directional.rs::t_discount_pct_removed_stub (line 5870)Asserts removed feature. Code-derived.(deprecated)DELETE — proves a stub, not a spec invariant
crates/pm-bot/src/domain/strategies/directional.rs::t_unified_offset_cents_from_ask_replaces_discount_pct (line 6217)Migration check. Code-derived.DIR-19 indirectDELETE once migration verified
crates/pm-bot/src/domain/strategies/directional.rs::t_stop_loss_emitted_on_first_fill (line 6257)Pure FSM-mutation on fill.DIR-14MEDIUM — rewrite as MOC.place_calls (sell decision reaches executor)
crates/pm-bot/src/domain/strategies/directional.rs::t_stop_loss_resized_on_subsequent_fill (line 6300)Pure resize.DIR-14, DIR-30MEDIUM
crates/pm-bot/src/domain/strategies/directional.rs::t_dca_replaces_buy_on_partial_fill (line 6351)Pure DCA decision.DIR-15, DIR-16MEDIUM — rewrite as LIV-3.3 cousin (DCA Decision reaches OrderClient)
crates/pm-bot/src/domain/strategies/directional.rs::t_dca_no_replace_when_remaining_below_min (line 6407)Pure threshold check.DIR-16LOW
crates/pm-bot/src/domain/strategies/directional.rs::t_recompute_exit_on_full_fill_is_off_by_default (line 6438)Pure flag.DIR-30LOW
crates/pm-bot/src/domain/strategies/directional.rs — remaining ~88 inline tests across lines 6438..endMostly pure evaluator gates / config tests / FSM transition assertions on synthetic state.DIR-* groupAggregate as Bucket B; rewrite priority MEDIUM if liveness-bridge is via executor wiring (LIV-30.2 per-strategy client_id)
crates/pm-bot/src/application/order_manager.rs::t_record_fill_* (lines 5470..5580, 4 tests)Pure ledger write check on synthetic state.G-26MEDIUM — promote to journal-observed
crates/pm-bot/src/application/order_manager.rs::t_autosell_* (lines 5558..5727, 6 tests)Pure tick-rounding math.BA-10LOW — math correct; bind to BA-10
crates/pm-bot/src/application/order_manager.rs::t_place_missing_exit_orders_skips_bucket_arb (line 5727)Pure routing decision.BA-8MEDIUM — rewrite as MOC.place_calls.is_empty() under bucket-arb
crates/pm-bot/src/application/order_manager.rs::t_record_fill_bucket_arb_full_fill_no_exit_state (line 5793)Pure state-mutation check.BA-8, G-27MEDIUM
crates/pm-bot/src/application/order_manager.rs::t_gamma_lag_* / t_admission_* / t_per_event_dedup_* / t_cumulative_budget_* / t_has_active_leg_* / t_zero_budget_skips_all (lines 7118..7339, ~10 tests)Admission gate decision-tree on synthetic state.BA-2, BA-11, BA-12, G-20MEDIUM — most belong to LIV-7.2 (strategy-level dedup); rewrite as MOC-observed
crates/pm-bot/src/application/order_manager.rs::t_classify_* / t_rejected_leg_* / t_expired_and_rejected_* / t_max_attempt_cap_* / t_classify_expiry_legs_posted_* (lines 7478..7646, ~10 tests)Pure classification of expiry/reject batches.BA-22LOW — classification math; pair with one BA-22 LIV
crates/pm-bot/src/application/order_manager.rs — remaining ~75 inline tests (across the 99-test span)Pure ledger / order-state / budget / dedup math.BA-*, G-26Aggregate Bucket B
crates/pm-bot/src/domain/strategies/bucket_arb/mod.rs (68 inline tests)Heavy on pure FSM evaluators (evaluate_tracking, evaluate_candidate); only a fraction observe MOC.BA-1..BA-22MEDIUM — most pair with LIV-12.1 (on_ws_book E2E) gap; rewrite top 30 as paired
crates/pm-bot/src/domain/strategies/bucket_arb/scanner_hooks.rs (25 inline tests, lines 1184..2317)These mostly do drive hooks and check Decision-set shape — they're closer to liveness than not, but assert on Vec<Decision> content rather than MOC.*BA-*Borderline; move 15 to Bucket A and 10 to Bucket B per fn-by-fn audit (deferred — tagged here as MEDIUM)
crates/pm-bot/src/domain/strategies/bucket_arb/fsm.rs::t_r8_2_live_sum_* (lines 657..816, 8 tests)Pure live-sum / staleness math.BA-13, G-28LOW
crates/pm-bot/src/domain/strategies/bucket_arb/fsm.rs::t_r8_2_drift_* (lines 835..890, 5 tests)Pure drift threshold math.BA-9LOW
crates/pm-bot/src/domain/strategies/bucket_arb/fsm.rs::t_task13_timeout_* (lines 921..984, 5 tests)Pure timeout math.BA-12LOW
crates/pm-bot/src/domain/strategies/maker_rebate.rs::t_maker_rebate_creation_works / t_maker_rebate_no_config_returns_empty / t_maker_rebate_no_repository_returns_empty (lines 1439..1460, 3 tests)Construction tests.MR-1, MR-4DELETE candidate — internal invariant
crates/pm-bot/src/domain/strategies/maker_rebate.rs::t_maker_rebate_webhook_* / t_maker_rebate_external_signal_* (lines 1541..1762, 5 tests)Driven through webhook/signal hooks — but still observed via Vec<Decision> length, not MOC.LIV-2.6, MR-26MEDIUM — rewrite to MOC-observed
crates/pm-bot/src/domain/strategies/maker_rebate.rs::t_maker_rebate_rtds_* (lines 1846..1982, 5 tests)RTDS-driven maker-rebate; same pattern as above — Decision-set assertion only.MR-14, LIV-1.7 cousinMEDIUM
crates/pm-bot/src/domain/strategies/maker_rebate.rs::t_periodic_* (lines 2105..2288, 7 tests)evaluate_periodic returns expected Decision shape.MR-1, MR-2, MR-3, MR-16, MR-17MEDIUM — pair with LIV-4.1, LIV-4.2 (full-pipeline executor wiring)
crates/pm-bot/src/domain/strategies/maker_rebate.rs::t_evaluate_rebate_pair_* (lines 2423..2466, 4 tests)Pure evaluate_rebate_pair.MR-2, MR-3LOW
crates/pm-bot/src/domain/strategies/maker_rebate.rs — remaining ~31 inline testsPure FSM transitions / evaluator math.MR-*Aggregate Bucket B
crates/pm-bot/src/domain/strategies/maker.rs (38 inline tests)Pure side-selector / price-mode / spread-filter math.MK-2..MK-9MEDIUM — pair with LIV-12.1
crates/pm-bot/src/domain/strategies/merge_arb.rs::t_band_exit_emits_cancel_merge_reconcile (line 1536, mentioned in audit)Decision-set assertion (merges.len() == 1).MA-8, MA-11MEDIUM — rewrite as MOC.merge_calls (LIV-5.3 / 5.4 dispatch)
crates/pm-bot/src/domain/strategies/merge_arb.rs — remaining ~18 testsPure FSM/admission math.MA-*Bucket B
crates/pm-bot/src/domain/liquidity.rs::test_l* / test_v* / test_s* (lines 534..881, ~49 tests)Pure liquidity scoring math (L1/L2/L3/L4 zones, signal aggregation). No I/O, no executor.(no rule-ID)LOW — these compute internal advisor signal; bind to a NEW rule-ID L-* if the signal influences decisions. Currently audited as NOT bound to any rule in test-coverage-matrix.md. Action: ADD L-1..L-10 rule group OR delete tests if signal is informational only.
crates/pm-bot/src/domain/strategies/shared/toxic_flow.rs::t_observe_* / t_dust_* / t_toxic_* / t_balanced_* / t_window_* / t_threshold_* (lines 229..405, 8 tests)Pure ToxicFlowWindow detector math.G-12LOW — math correct; pair with one LIV-1.5 driver
crates/pm-bot/src/domain/strategies/extensions/dca.rs::t_trigger_* / t_buy_* / t_locked_profit_* / t_default_* / t_mock_strategy_* (lines 291..648, 24 tests)Pure DCA trigger / size / price / locked-profit math.DIR-15, DIR-16MEDIUM — pair with one LIV-3.3 cousin (DCA Decision reaches OrderClient)
crates/pm-bot/src/domain/strategies/shared/budget.rs (9 inline tests)Pure budget predicate math.G-20LOW — math; pair with tests/budget_concurrent_admission.rs
crates/pm-bot/src/domain/strategies/shared/tiered_exit.rs (17 inline tests)Pure tiered-exit math.G-27, DIR-30LOW
crates/pm-bot/src/domain/strategies/shared/time.rs (8 inline tests)Pure deadline math.G-1LOW — pair with timezone/mod.rs
crates/pm-bot/src/domain/strategies/shared/pricing.rs (7 inline tests)Pure pricing helpers.(no rule-ID)LOW — internal helper; bind to MK-7/MK-8 if used in price_mode
crates/pm-bot/src/domain/strategies/shared/orderbook.rs (11 inline tests)Pure orderbook accessor math.BA-21LOW
crates/pm-bot/src/domain/strategies/shared/usdc.rs / shared/redeem.rs / shared/clob_io.rs / shared/fair_value.rs / shared/market_type_filter.rs / shared/webhook_seeding.rs (~22 tests across)Pure helpers.various G-* / per-strategyLOW
crates/pm-bot/src/domain/market_cache.rs (16 inline tests)Pure cache-mutation tests.(no rule-ID — internal)DELETE candidate or bind to G-28 if cache freshness is contracted
crates/pm-bot/src/io/ws/book.rs (35 inline tests)Pure WS book parser tests.(no rule-ID)LOW — external-contract relevant; consider proptest
crates/pm-bot/src/io/ws/user.rs::test_parse_* / test_condition_id_strips_0x / test_trade_without_maker_orders (lines 457..624, 9 tests)Pure parse tests on user WS payload.(LIV-12.3 producer side — partial)LOW — same RTDS class; needs liveness-pair
crates/pm-bot/src/io/rtds/parse.rs::t_parse_negative_size_rejected / t_parse_price_over_one_rejected / t_rejects_unknown_topic / t_rejects_malformed_json / t_type_label_frozen / t_rejects_missing_* (lines 512..630, 6 tests)Pure parse-error path tests.G-12LOW — same RTDS class
crates/pm-bot/src/io/rtds/metrics.rs::t_metrics_initialized_with_all_counters_zero ... t_bucket_arb_dirty_marks_increment (lines 708..826, 7 tests)Pure counter snapshot tests; manually seeded counters per existing audit §4.LIV-1.8MEDIUM — rewrite as parser→hub→tick chain (already covered partially by tests/rtds_hook_e2e.rs for parser side, gap is hub→tick)
crates/pm-bot/src/io/rtds/subscribe.rs::t_build_subscribe_event_slug / t_build_unsubscribe_market_slug / t_build_message_* / t_filter_set_replace_commits_state (lines 354..542, 6 tests beyond the 3 already in Bucket A)Pure frame-building helpers.LIV-1.2, LIV-49.1LOW
crates/pm-bot/src/io/rtds/smoke.rs (3 inline tests)Smoke-report shape.RCA §6.2 LIV-22.1MEDIUM — should be live-network probe; smoke output shape is partial
crates/pm-bot/src/io/rtds/client.rs (4 inline tests)Client construction / state-machine pure asserts.LIV-1.1MEDIUM — full-pipeline TCP-bind / connect needs live-mock
crates/pm-bot/src/io/rtds/dedupe.rs (8 inline tests, all in Bucket A above)(already classified Bucket A)LIV-36.1—
crates/pm-bot/src/io/webhook/server.rs::t_returns_* (already in Bucket A — 13 tests)(Bucket A)——
crates/pm-bot/src/io/webhook/dedupe.rs (5 inline tests, in Bucket A)(Bucket A)LIV-7.1—
crates/pm-bot/src/io/webhook/auth.rs (6 inline tests, in Bucket A)(Bucket A)LIV-2.2—
crates/pm-bot/src/io/webhook/signal.rs (9 inline tests)Pure signal-shape tests.LIV-2.5LOW
crates/pm-bot/src/io/webhook/metrics.rs (5 inline tests)Pure counter-shape tests.LIV-2.*LOW
crates/pm-bot/src/io/gamma_client.rs (22 inline tests)Pure parsing / URL-building tests.(no rule-ID — external contract)LOW; consider golden-fixture probe per RCA §6.4
crates/pm-bot/src/io/clob/dry_run.rs::t_sim_* / t_maker_* / t_taker_* (Bucket A above for 6 tests)(already classified Bucket A)G-3, G-16—
crates/pm-bot/src/io/clob/dry_run.rs — remaining 8 inline testsPure simulator state asserts.G-3LOW
crates/pm-bot/src/io/clob/simulator/mock_clob.rs — 6 tests beyond Bucket APure mock-CLOB state.(no rule-ID — internal)LOW
crates/pm-bot/src/io/native_balance.rs (3 inline tests)Pure balance helper.(no rule-ID)LOW
crates/pm-bot/src/observability/metrics.rs (12 inline tests)Pure metrics struct tests.(no rule-ID)LOW
crates/pm-bot/src/observability/logger.rs (2 inline tests)Pure logger config.(no rule-ID)DELETE candidate
crates/pm-bot/src/persistence/db/schema.rs::test_*_check_constraint / test_*_unique_* (lines 451..723, ~15 tests)DB constraint tests — these ARE liveness-style (insert+observe) but assert on internal schema, not strategy spec.G-31 indirectLOW — keep as schema regression suite
crates/pm-bot/src/persistence/db/queries.rs::test_strategy_pnl_summary_* / test_daily_pnl_* / test_price_history_* / test_closed_positions_* / test_open_positions* / test_orders_* (lines 555..809, 23 tests)Pure SQL query tests — driven through SQLite but assert on rows, not on UX/strategy.(no rule-ID — internal)LOW — keep as DB-query regression suite; bind to ADR-014 if PnL math is rule-bound
crates/pm-bot/src/persistence/db/writer.rs (27 inline tests)DB writer tests — same class.(no rule-ID)LOW — Bucket B by virtue of being internal, not by being pure
crates/pm-bot/src/persistence/wallets.rs — remaining ~23 tests beyond Bucket ADB CRUD.ADR-015 §D1LOW — keep as regression
crates/pm-bot/src/persistence/strategy_config.rs — remaining ~30 tests beyond Bucket ADB CRUD + extract_definition_from_env.ADR-015 §D6LOW
crates/pm-bot/src/persistence/journal/gc.rs (10 inline tests)Pure GC math.ADR-009 §retentionLOW
crates/pm-bot/src/persistence/journal/reader.rs (5 inline tests)DB reader.ADR-017LOW (already in Bucket A)
crates/pm-bot/src/persistence/capture/redact.rs (5 inline tests)Pure redaction.(no rule-ID)LOW
crates/pm-bot/src/persistence/capture/sink.rs (3 inline tests)Pure sink tests.LIV-8.1LOW
crates/pm-bot/src/persistence/capture/writer.rs (2 inline tests)Pure writer state.LIV-8.1LOW
crates/pm-bot/src/persistence/position_store.rs (4 inline tests)Pure position-store CRUD.(no rule-ID)LOW
crates/pm-bot/src/application/auto_prune.rs (6 inline tests)Pure auto-prune logic.ADR-009 §retentionLOW
crates/pm-bot/src/application/dispatcher.rs::t_from_env_* (lines 360..382, 4 tests)Pure env-→-StrategyImpl mapping.G-5, ADR-008LOW — bind to G-5
crates/pm-bot/src/application/scanner.rs — 5 tests (3 in Bucket A) — remaining 2Pure resolution-sweep tests.BA-17LOW
crates/pm-bot/src/application/alerts/sender.rs (3 inline tests)Pure sender abstraction.(no rule-ID)LOW
crates/pm-bot/src/application/alerts/sink.rs (2 inline tests)Pure sink abstraction.(no rule-ID)LOW
crates/pm-bot/src/application/alerts/alert.rs (3 inline tests)Pure alert struct.(no rule-ID)LOW
crates/pm-bot/src/application/alerts/metrics_tap.rs (3 inline tests)Pure tap.(no rule-ID)LOW
crates/pm-bot/src/application/alerts/engine.rs (6 inline tests)Pure rule-evaluation orchestration. Some are Bucket A (RTDS health) — remaining 0 in B.(covered)—
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_* — 8 tests (all in Bucket A)(Bucket A)LIV-1.9—
crates/pm-bot/src/application/alerts/rule.rs — remaining 13 testsOther rule-evaluations (drawdown, posts_err_spike, emergency_stop, position_closed).(alerts taxonomy — no rule-ID yet)MEDIUM — bind to alerts spec ID once authored
crates/pm-bot/src/application/action_executor.rs (6 inline tests)Pure action-executor wrapper logic.(no rule-ID)LOW
crates/pm-bot/src/application/capture.rs (2 inline tests)Pure capture-config tests.LIV-8.*LOW
crates/pm-bot/src/application/backtest/clock.rs / engine.rs / rng.rs / source_mux.rs (~18 inline tests)Pure backtest harness.LIV-31.2, LIV-31.3, G-30LOW — keep as backtest regression
crates/pm-bot/src/application/engine.rs — remaining 2 tests beyond Bucket APure config tests.(no rule-ID)LOW
crates/pm-bot/src/application/order_manager.rs — large remainder (~75 tests)Pure order/budget/dedup math.various G-* / BA-*MEDIUM — these are the heart of basket-arb internal correctness; pair w/ LIV-41.* per integration tests already in Bucket A
crates/pm-bot/src/domain/timestamped.rs (3 inline tests)Pure helper.G-30LOW
crates/pm-bot/src/domain/stats.rs (1 inline test)Pure stats.(no rule-ID)LOW
crates/pm-bot/src/domain/strategies/contexts.rs (2 inline tests)Pure context construction.(no rule-ID)DELETE candidate
crates/pm-bot/src/domain/strategies/decision.rs (2 inline tests)Pure Decision construction.G-4LOW
crates/pm-bot/src/domain/strategies/repository.rs (7 inline tests)Pure repository wrapper.(no rule-ID)LOW
crates/pm-bot/src/domain/strategies/scanner_hooks.rs (6 inline tests)Pure trait test fixtures.(LIV-12.* producer side)MEDIUM — tied to LIV-12 gap
crates/pm-bot/src/domain/strategies/bucket_arb/decision_log.rs (2 inline tests)Pure log struct.(no rule-ID)LOW
crates/pm-bot/src/config/env.rs / loader.rs / mod.rs / rtds.rs / webhook.rs / capture.rs / validate.rs / strategy/mod.rs (~31 inline tests across)Env-parsing tests; some assert fail-fast (Bucket A candidates), most assert pure parse.G-6, G-7, G-31, LIV-17.1, LIV-23.*MEDIUM — fail-fast tests (~15) belong in Bucket A as panic.fail_fast boundary; pure parse tests stay in Bucket B
crates/pm-bot/src/presentation/tui.rs (3 inline tests)TUI rendering helpers.(no rule-ID)LOW — UI presentation
crates/pm-common/src/pricing.rs::test_* (lines unspecified, 40 tests)Pure pricing math.(no rule-ID — math primitive)LOW — math primitive; bind to G-* if used in critical decision paths
crates/pm-common/src/error.rs (9 tests)Pure error wrapper.(no rule-ID)LOW
crates/pm-common/src/resilience.rs (8 tests)Pure resilience helpers.DIR-25LOW
crates/pm-common/src/models/order.rs (8 tests)Pure model serialisation.G-32 (sonic-rs)LOW
crates/pm-common/src/models/strategy.rs (6 tests)Pure model. CLAUDE.md grep gate enforces no end_date — these tests validate other fields.G-2LOW (G-2 already enforced via grep gate)
crates/pm-common/src/retry.rs (3 tests)Pure retry math.DIR-25LOW
crates/pm-sdk/src/clob/wire.rs (11 tests)Pure JSON wire-shape tests.BA-15, BA-16LOW — pair with golden fixture probe (RCA §6.4 P2)
crates/pm-sdk/src/validation.rs (9 tests)Pure ClobAuth validation rules.BA-15, BA-16LOW
crates/pm-sdk/src/eip712/order.rs (9 tests)Pure EIP-712 hash tests.BA-16LOW — math primitive; bind to BA-16
crates/pm-sdk/src/eip712/mod.rs (7 tests)Pure typehash tests.BA-16LOW
crates/pm-sdk/src/crypto/mod.rs (8 tests)Pure crypto primitive.(no rule-ID)LOW
crates/pm-sdk/src/auth/credentials_l1.rs (8 tests)Pure L1 credential.BA-16LOW
crates/pm-sdk/src/auth/key_utils.rs (7 tests)Pure key util.(no rule-ID)LOW
crates/pm-sdk/src/auth/cache.rs (3 tests)Pure cache.(no rule-ID)LOW
crates/pm-sdk/src/onchain/redeem.rs (7 tests)Pure redeem call construction.G-19, LIV-43.*MEDIUM — pair with executor-side liveness already in Bucket A
crates/pm-sdk/src/onchain/wrap.rs (3 tests)Pure wrap call.LIV-45.1LOW — paired with manual smoke 2026-05-01
crates/pm-sdk/src/onchain/ctf.rs (3 tests)Pure CTF call construction.G-18, LIV-5.3MEDIUM — pair with merge-arb dispatch test gap
crates/pm-sdk/src/onchain/balance.rs (3 tests)Pure balance call.LIV-29.5LOW
crates/pm-sdk/src/parsers.rs (5 tests)Pure parsers.(no rule-ID)LOW
crates/pm-sdk/src/clob/mod.rs (5 tests)Pure CLOB module.(no rule-ID)LOW
crates/pm-sdk/src/config.rs (4 tests)Pure config.(no rule-ID)LOW
crates/pm-analytics/src/sweep/grid.rs (7 tests)Pure grid expansion.LIV-19.1LOW
crates/pm-analytics/src/whatif/filter.rs (6 tests)Pure filter.LIV-19.3LOW
crates/pm-analytics/src/whatif/diff.rs (5 tests)Pure diff.LIV-19.3LOW
crates/pm-analytics/src/whatif/journal_reader.rs (4 tests)Pure journal reader.LIV-19.3LOW
crates/pm-analytics/src/sweep/report.rs (1 test)Pure report.LIV-19.2LOW

Bucket B detailed expansion — directional.rs (top files for per-fn audit) ​

The 128 inline tests in crates/pm-bot/src/domain/strategies/directional.rs are the largest single concentration of pure-evaluator unit tests in the workspace. Most assert on the return value of pure evaluators (evaluate_signal, evaluate_periodic, compute_sweep, compute_watch_arm_fak_limit, validate_invariants) over a constructed input; they do not drive that output through OrderClient::place_limit_order / cancel_order and do not observe a journal row, SSE frame, log line, or metric counter. Rewrite priorities in this section split by sub-feature:

Test (file:fn)Why tautologySuggested rule-IDRewrite priority
directional.rs::t_recompute_exit_resizes_on_each_fill (line 6483)Pure FSM-mutation (exit price recompute on fill) on synthetic StrategyState. Does not assert MOC.place_calls or journal.DIR-30MEDIUM — pair with LIV-12.3 own-fill routing
directional.rs::t_legacy_harvester_profile_unchanged_with_defaults (line 6544)Asserts profile equivalence on returned Decision shape. Strong rule-binding (DIR-19 profile equivalence) but pure.DIR-19MEDIUM — pair with LIV-8.3 replay parity (already mapped)
directional.rs::t_periodic_emits_for_admitted_market (line 6683, async)evaluate_periodic returns expected Decision under admitted market — pure. Does not assert MOC.place_calls.DIR-13, LIV-3.3MEDIUM — pair with LIV-3.*
directional.rs::t_periodic_skips_already_held_market (line 6745, async)Pure skip-decision on synthetic position.DIR-13 (negative — already held)MEDIUM
directional.rs::t_periodic_filters_below_min_probability (line 6802, async)Pure threshold filter — DIR-5 negative.DIR-5LOW
directional.rs::t_record_pending_entry_dedupes_repeated_calls (line 6885)Pure dedup state-mutation.LIV-7.2 cousinMEDIUM
directional.rs::t_has_active_entry_covers_pending_live_and_positions (line 6902)Pure predicate over union of states.DIR-15 cousinLOW
directional.rs::t_after_execute_promotes_entry_pending_to_live (line 6945)Pure state-mutation; FSM transition.DIR-22 (recovery) cousinMEDIUM
directional.rs::t_after_execute_failed_place_clears_pending (line 6974)Pure cleanup on failure.DIR-25 (resilience)LOW
directional.rs::t_after_execute_writes_exit_stop_dca_order_ids (line 6995)Pure order-id bookkeeping.DIR-22LOW
directional.rs::t_ttl_only_evicts_awaiting_ack (line 7060)Pure TTL eviction.DIR-25LOW
directional.rs::t_full_fill_removes_pending_but_position_blocks_reentry (line 7097)Pure FSM state.LIV-7.2 cousinMEDIUM
directional.rs::t_partial_fill_keeps_pending_lock (line 7136)Pure FSM state.DIR-30LOW
directional.rs::t_on_cancel_clears_pending_lock (line 7181)Pure cleanup on cancel.DIR-22LOW
directional.rs::t_invariant_stop_lt_min_entry_violated (line 7262)Pure invariant validator (negative).DIR-12LOW — keep as invariant suite
directional.rs::t_invariant_stop_eq_min_entry_violated (line 7286)Pure invariant validator.DIR-12LOW
directional.rs::t_invariant_stop_lt_max_entry_violated_no_min (line 7300)Pure.DIR-12LOW
directional.rs::t_invariant_exit_gt_max_entry_violated (line 7318)Pure.DIR-11, DIR-12LOW
directional.rs::t_invariant_min_lt_max_violated (line 7337)Pure.DIR-12LOW
directional.rs::t_invariant_price_out_of_range (line 7356)Pure.DIR-12LOW
directional.rs::t_invariants_all_pass_for_harvester_preset (line 7404)Pure (positive case).DIR-12LOW
directional.rs::t_pending_evict_log_on_ttl (line 7430)Pure log-emit on TTL — but log capture is NOT via TestWriter, only inspects in-memory state.DIR-25MEDIUM — rewrite to capture log via TestWriter (LOG boundary)
directional.rs::t_seen_cids_increments_on_reprice (line 7471, async)Pure counter increment.(no rule-ID — internal)LOW
directional.rs::t_reject_counters_track_repriced (line 7528, async)Pure counter.DIR-25LOW
directional.rs::t_pending_lifecycle_full (line 7594, async)Pure lifecycle traversal.DIR-22MEDIUM
directional.rs::t_sweep_branch_forces_post_only_false_when_limit_post_only_set (line 7704)Pure flag-routing in compute_sweep.DIR-31 cousinLOW
directional.rs::t_validate_invariants_rejects_sweep_limit_post_only_combo (line 7781)Pure validator.DIR-12LOW
directional.rs::t_watch_arm_fak_admits_to_watchlist_at_watch_prob (line 7940)Pure FSM admission (ADR-017).ADR-017 (proposed → accepted)MEDIUM — pair with tests/watch_arm_fak_recovery.rs (Bucket A)
directional.rs::t_watch_arm_fak_arms_when_best_ask_crosses_trigger (line 7958)Pure trigger logic.ADR-017LOW
directional.rs::t_watch_arm_fak_emits_fak_with_post_only_false (line 7989)Pure Decision shape.ADR-017, G-16 (negative — FAK is taker)LOW
directional.rs::t_watch_arm_fak_limit_price_capped_by_max_entry_price (line 8010)Pure min() composition.DIR-6LOW
directional.rs::t_watch_arm_fak_limit_price_capped_by_arm_limit_max_price (line 8035)Pure min() composition.ADR-017LOW
directional.rs::t_watch_arm_fak_revalidate_drops_if_prob_decreased (line 8058)Pure revalidation.ADR-017, DIR-5LOW
directional.rs::t_watch_arm_fak_revalidate_drops_if_max_hours_exceeded (line 8085)Pure deadline check; touches dead-code field DIR-7.DIR-7 (gap)DELETE candidate per memory max_hours_dead_code
directional.rs::t_watch_arm_fak_timeout_expires_watchlist (line 8115)Pure timeout logic.ADR-017LOW
directional.rs::t_watch_arm_fak_no_retry_after_partial_fill_done (line 8151)Pure no-retry logic.DIR-22 (recovery)MEDIUM
directional.rs::t_watch_arm_fak_trigger_best_bid_fires_when_bid_crosses (line 8222)Pure trigger logic.ADR-017LOW
directional.rs::t_watch_arm_fak_trigger_mid_fires_when_midpoint_crosses (line 8262)Pure trigger logic.ADR-017LOW
directional.rs::t_watch_arm_fak_trigger_last_trade_fires_when_present (line 8308)Pure trigger logic.ADR-017LOW
directional.rs::t_watch_arm_fak_deadline_cutoff_uses_local_midnight (line 8369)Pure deadline math via TZ cache.G-1, ADR-016MEDIUM — pair with LIV-9.3
directional.rs::t_validate_invariants_rejects_arm_post_only_true_with_watch_arm_fak (line 8422)Pure validator.DIR-12LOW
directional.rs::t_validate_invariants_rejects_arm_order_type_gtd_with_watch_arm_fak (line 8437)Pure validator.DIR-12LOW
directional.rs::t_validate_invariants_rejects_sweep_with_watch_arm_fak (line 8450)Pure validator.DIR-12LOW
directional.rs::t_watch_arm_fak_min_probability_ignored_with_info_log (line 8466)Pure flag + info log. Borderline — info log emit is observable.ADR-017MEDIUM — could be LOG boundary
directional.rs::t_watch_arm_fak_skips_when_limit_above_ev_cap (line 8483)Pure ADR-017 EV-cap composition.ADR-017LOW
directional.rs::t_watch_arm_fak_exit_uses_filled_size_not_original_size (line 8579)Pure size logic.DIR-22LOW
directional.rs::t_watch_arm_fak_revalidate_after_fill_passes_emits_exit (line 8673, async)Pure revalidation + Decision shape.ADR-017, DIR-30MEDIUM
directional.rs::t_watch_arm_fak_revalidate_after_fill_fails_cancels_exit (line 8745, async)Pure revalidation + cancel Decision.ADR-017MEDIUM
directional.rs::t_watch_arm_fak_emits_state_transition_event_on_admit (line 8911)Pure event emit on FSM transition.ADR-017MEDIUM — pair with journal observation
directional.rs::t_watch_arm_fak_recovery_skips_terminal_cids (line 8963)Pure recovery skip logic.G-25, ADR-017MEDIUM — pair with tests/watch_arm_fak_recovery.rs (Bucket A)
directional.rs::t_directional_webhook_triggers_evaluate (line 9106, async)Webhook → strategy hook returning Decision.LIV-2.6, DIR-23MEDIUM — rewrite as MOC.place_calls
directional.rs::t_directional_webhook_rejects_bucket_signal (line 9200, async)LIV-2.5 (strategy scope mismatch — negative).LIV-2.5MEDIUM
directional.rs::t_directional_webhook_market_not_in_cache_rejected (line 9228, async)Pure rejection.LIV-2.5 cousinLOW
directional.rs::t_directional_webhook_dedup_when_position_open (line 9259, async)Pure dedup.LIV-7.2MEDIUM
directional.rs::t_directional_external_signal_market_in_cache_no_fetch (line 9373, async)Pure cache-hit path.LIV-2.6, DIR-23MEDIUM
directional.rs::t_directional_external_signal_market_not_in_cache_fetches_and_evaluates (line 9413, async)Pure cache-miss + fetch.LIV-2.6, DIR-23MEDIUM
directional.rs::t_directional_external_signal_fetch_fails_returns_empty (line 9454, async)Pure error path.DIR-25LOW
directional.rs::t_directional_rtds_below_threshold_no_action (line 9548, async)Pure RTDS toxic-flow filter (negative).G-12, DIR-24MEDIUM — pair with tests/rtds_active_cancel.rs (Bucket A)
directional.rs::t_directional_rtds_dust_filtered_no_window_growth (line 9580, async)Pure dust filter.LIV-1.5LOW
directional.rs::t_directional_rtds_above_threshold_log_only_default_returns_empty (line 9612, async)Pure log-only filter.G-13 (negative)MEDIUM
directional.rs::t_directional_rtds_above_threshold_active_cancel_emits_cancels (line 9652, async)Active-cancel emit Decision — but synthetic, not via executor.G-13, DIR-24MEDIUM — pair with tests/rtds_active_cancel.rs::t_toxic_flow_emits_cancel_when_active
directional.rs::t_directional_rtds_orders_matched_same_path_as_trade (line 9711, async)Pure path equivalence.LIV-1.4, ADR-004 Q2LOW
directional.rs::t_last_trade_price_populated_from_ws_market_trade_event (line 9830)Pure populate.LIV-12.3LOW
directional.rs::t_last_trade_price_populated_from_rtds_trade_event (line 9855)Pure populate from RTDS.LIV-1.4MEDIUM
directional.rs::t_last_trade_price_returns_none_when_stale_beyond_ttl (line 9881)Pure TTL.G-28LOW
directional.rs::t_watch_arm_fak_trigger_last_trade_now_fires_in_e2e (line 9928)Pure E2E-shape composition (still synthetic).ADR-017MEDIUM
directional.rs::t_ev_cap_disabled_when_source_unset (line 10143)Pure flag.ADR-017LOW
directional.rs::t_ev_cap_log_only_logs_would_reject_but_emits_anyway (line 10169)Pure log + emit. Borderline — log boundary candidate.ADR-017MEDIUM
directional.rs::t_ev_cap_active_skips_emit_when_limit_exceeds_ev (line 10202)Pure skip.ADR-017LOW
directional.rs::t_ev_cap_active_emits_when_limit_within_ev (line 10236)Pure emit.ADR-017LOW
directional.rs::t_validate_invariants_rejects_unsupported_fair_value_source (line 10270)Pure validator.DIR-12LOW
directional.rs::t_validate_invariants_accepts_both_ev_cap_mode_variants (line 10293)Pure validator.DIR-12LOW
directional.rs::t_validate_invariants_rejects_negative_min_edge_abs (line 10314)Pure validator.DIR-12LOW
directional.rs::t_validate_invariants_rejects_negative_min_roi (line 10331)Pure validator.DIR-12LOW
directional.rs::t_ev_cap_skipped_when_producer_returns_none (line 10351)Pure None-handling.ADR-017LOW
directional.rs::t_ev_cap_uses_adr017_defaults_when_knobs_unset (line 10392)Pure defaults.ADR-017LOW
directional.rs::t_seed_active_states_pre_populates_watch_states_map (line 10486)Pure state-seed.G-25, ADR-017MEDIUM
directional.rs::t_recovered_watching_state_preserves_since_timestamp_for_timeout_check (line 10543)Pure since-timestamp preservation.G-25, ADR-017LOW
directional.rs::t_seed_active_states_skips_blacklisted_cids (line 10583)Pure blacklist filter.G-25LOW

(The directional.rs section above lists 60+ inline tests; the remaining ~50 follow the same pattern — pure evaluator on synthetic state. All bucket as B.)

Bucket B detailed expansion — order_manager.rs ​

Test (file:fn)Why tautologySuggested rule-IDRewrite priority
order_manager.rs::t_apply_partial_fill_shrinks_remaining (line 7679)Pure remaining-size update.G-26MEDIUM — pair with tests/partial_fill_history.rs (Bucket A)
order_manager.rs::t_apply_partial_fill_zero_size_treats_as_full (line 7728)Pure edge case.G-26LOW
order_manager.rs::t_partial_fill_then_expire_updates_basket_legs_size (line 7766)Pure update on expire.G-26, BA-22LOW
order_manager.rs::t_apply_partial_fill_overfill_clamps_to_full (line 7825)Pure clamp.G-26LOW
order_manager.rs::t_unknown_status_ignored (line 7856)Pure unknown-status no-op.G-4LOW
order_manager.rs::t_partial_fill_then_expire_repost_uses_remaining_size (line 7908)Pure repost size.BA-22MEDIUM
order_manager.rs::t_repeated_partial_fills_accumulate_position (line 8018)Pure accumulation.G-26LOW
order_manager.rs::t_partial_fills_weighted_avg_price (line 8102)Pure weighted avg.ADR-014 PnLLOW
order_manager.rs::t_mark_basket_dirty_is_idempotent (line 8171)Pure idempotency.BA-13LOW
order_manager.rs::t_mark_basket_dirty_empty_slug_noop (line 8195)Pure no-op edge.BA-13LOW
order_manager.rs::t_partial_placement_marks_basket_dirty (line 8212)Pure mark.BA-22LOW
order_manager.rs::t_td2_repost_ceiling_patient_maker_uses_min_profit_pct (line 8272)Pure repost ceiling.BA-4LOW
order_manager.rs::t_td2_repost_ceiling_immediate_fill_uses_target_profit (line 8285)Pure repost ceiling.BA-4LOW
order_manager.rs::t_td2_repost_ceiling_immediate_fill_target_profit_dominates_min_profit (line 8300)Pure dominance rule.BA-4LOW
order_manager.rs::t_td2_repost_ceiling_immediate_fill_zero_target_profit_collapses_to_one (line 8319)Pure edge.BA-4LOW

(Remaining 30+ inline tests in order_manager.rs all of similar pure-state-mutation shape; bucket as B.)

Bucket B detailed expansion — bucket_arb/mod.rs ​

Test (file:fn)Why tautologySuggested rule-IDRewrite priority
bucket_arb/mod.rs::t_evaluate_basket_ready_3legs (line 1381)Pure FSM evaluator returns Ready.BA-3, BA-4MEDIUM — pair with LIV-12.1
bucket_arb/mod.rs::t_evaluate_basket_sum_too_high (line 1418)Pure threshold.BA-4 (negative)LOW
bucket_arb/mod.rs::t_evaluate_basket_profit_too_low (line 1439)Pure threshold.BA-4, BA-21LOW
bucket_arb/mod.rs::t_evaluate_basket_over_budget (line 1470)Pure budget gate.G-20, BA-11LOW
bucket_arb/mod.rs::t_evaluate_basket_missing_book (line 1496)Pure missing-book skip.G-28LOW
bucket_arb/mod.rs::t_evaluate_basket_missing_book_zero_bid (line 1512)Pure edge.G-28LOW
bucket_arb/mod.rs::t_evaluate_basket_empty_legs_is_missing_book (line 1528)Pure edge.BA-14LOW
bucket_arb/mod.rs::t_immediate_fill_ready_when_sum_plus_target_le_one (line 1541)Pure threshold.BA-4LOW
bucket_arb/mod.rs::t_immediate_fill_skip_when_margin_insufficient (line 1577)Pure margin gate.BA-4LOW
bucket_arb/mod.rs::t_immediate_fill_sum_above_one_still_skips (line 1606)Pure rule.BA-4LOW
bucket_arb/mod.rs::t_immediate_fill_missing_ask_returns_missing_book (line 1626)Pure edge.BA-2LOW
bucket_arb/mod.rs::t_immediate_fill_respects_budget (line 1642)Pure budget gate.G-20, BA-11LOW
bucket_arb/mod.rs::t_patient_mode_unchanged_after_pricing_mode_introduction (line 1668)Pure regression — mode equivalence.BA-25 (replay parity cousin)LOW
bucket_arb/mod.rs::t_strategy_creation_works (line 1695)Construction.(internal)DELETE candidate
bucket_arb/mod.rs::t_strategy_clone_shares_state (line 1705)Construction.(internal)DELETE candidate
bucket_arb/mod.rs::t_on_orderbook_update_unknown_event_returns_empty (line 1720)Pure unknown-event no-op.G-4LOW
bucket_arb/mod.rs::t_on_orderbook_update_known_event_step45a_still_empty (line 1739)Step-45a stub assertion.(deferred)DELETE candidate (asserts stub state)
bucket_arb/mod.rs::t_on_partial_fill_no_autosell_holds (line 1843)Pure hold.BA-8, BA-10LOW
bucket_arb/mod.rs::t_on_partial_fill_with_autosell_emits_gtc_sell (line 1860)Pure Decision shape.BA-10MEDIUM — pair with tests/strategy_engine_integration.rs::engine_handle_full_fill_with_autosell_places_sell (Bucket A)
bucket_arb/mod.rs::t_on_partial_fill_unknown_order_is_noop (line 1893)Pure no-op.G-4LOW
bucket_arb/mod.rs::t_on_full_fill_marks_basket_filled_when_all_legs_done (line 1907)Pure FSM state.BA-6MEDIUM
bucket_arb/mod.rs::t_on_full_fill_with_autosell_sells_remaining_delta (line 1927)Pure Decision shape.BA-10MEDIUM
bucket_arb/mod.rs::t_on_cancel_marks_leg_and_abandons_when_no_activity (line 1955)Pure cancel handling.BA-22, BA-9LOW
bucket_arb/mod.rs::t_on_cancel_keeps_basket_when_filled_legs_exist (line 1976)Pure abandon-vs-keep.BA-22LOW
bucket_arb/mod.rs::t_on_market_resolved_emits_redeem_per_filled_leg (line 2003)Pure redeem Decision.BA-17MEDIUM — pair with application/executor.rs::t_redeem_* (Bucket A)
bucket_arb/mod.rs::t_on_market_resolved_three_filled_legs_emits_three_redeems (line 2050)Pure scaling.BA-17LOW
bucket_arb/mod.rs::t_on_tick_size_change_updates_leg_tick_no_cancel_when_on_grid (line 2131)Pure tick-size update.BA-9LOW
bucket_arb/mod.rs::t_on_tick_size_change_off_grid_emits_cancel (line 2150)Pure off-grid cancel.BA-9LOW
bucket_arb/mod.rs::t_evaluate_periodic_fresh_basket_is_noop (line 2179)Pure freshness gate.BA-12LOW
bucket_arb/mod.rs::t_evaluate_periodic_emits_cancels_after_timeout (line 2189)Pure timeout cancel.BA-12MEDIUM — pair with tests/strategy_engine_integration.rs::engine_periodic_after_timeout_emits_cancels_to_executor (Bucket A)
bucket_arb/mod.rs::t_evaluate_periodic_skips_filled_basket_after_timeout (line 2214)Pure skip-when-filled.BA-22LOW
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_patient_uses_maker_safe (line 2278)Pure mode routing.BA-21LOW
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_immediate_fill_uses_best_ask (line 2290)Pure mode routing.BA-21LOW
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_returns_none_on_missing_book (line 2302)Pure None edge.G-28LOW
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_returns_none_on_stale_book (line 2316)Pure stale guard.G-28LOW
bucket_arb/mod.rs::t_td3_should_cancel_patient_below_ceiling_keeps_basket (line 2332)Pure threshold.BA-9LOW
bucket_arb/mod.rs::t_td3_should_cancel_patient_above_ceiling_plus_margin_cancels (line 2341)Pure threshold.BA-9LOW
bucket_arb/mod.rs::t_td3_should_cancel_patient_at_margin_boundary_keeps_basket (line 2350)Pure boundary.BA-9LOW
bucket_arb/mod.rs::t_td3_should_cancel_immediate_fill_below_ceiling_keeps_basket (line 2359)Pure threshold.BA-9LOW
bucket_arb/mod.rs::t_td3_should_cancel_immediate_fill_above_threshold_cancels (line 2369)Pure threshold.BA-9LOW
bucket_arb/mod.rs::t_td3_should_cancel_immediate_fill_at_admission_boundary_keeps_basket (line 2377)Pure boundary.BA-9LOW
bucket_arb/mod.rs::t_td3_evaluate_periodic_emits_drift_cancels_patient_mode (line 2388)Pure drift cancel.BA-9MEDIUM
bucket_arb/mod.rs::t_td3_evaluate_periodic_emits_drift_cancels_immediate_fill_mode (line 2413)Pure drift cancel.BA-9MEDIUM
bucket_arb/mod.rs::t_td3_evaluate_periodic_no_drift_emits_no_cancels (line 2436)Pure negative.BA-9LOW
bucket_arb/mod.rs::t_td3_evaluate_periodic_skips_drift_when_repository_absent (line 2453)Pure absent-repo skip.(config-edge)LOW
bucket_arb/mod.rs::t_td3_evaluate_periodic_skips_filled_basket_no_drift_cancel (line 2471)Pure no-cancel-on-filled.BA-22LOW
bucket_arb/mod.rs::t_recover_returns_empty_step45b_stub (line 2497)Stub assertion.(deferred)DELETE candidate
bucket_arb/mod.rs::t_autosell_none_pct_holds (line 2508)Pure flag.BA-10LOW
bucket_arb/mod.rs::t_autosell_one_percent_tick_001 (line 2513)Pure tick math.BA-10LOW
bucket_arb/mod.rs::t_autosell_sub_tick_pct_clamps_to_tick_capped (line 2522)Pure clamp.BA-10LOW
bucket_arb/mod.rs::t_autosell_invalid_pct_returns_none (line 2532)Pure validation.BA-10LOW
bucket_arb/mod.rs::t_mark_basket_posted_transitions_tracking_to_posted (line 2558)Pure FSM transition.BA-6MEDIUM
bucket_arb/mod.rs::t_mark_basket_posted_noop_on_already_participating (line 2580)Pure idempotency.BA-22LOW
bucket_arb/mod.rs::t_mark_basket_posted_noop_on_missing_basket (line 2604)Pure no-op.(defensive)LOW
bucket_arb/mod.rs::t_st01_mixed_tick_per_bucket_rounding (line 2660)Pure rounding.BA-21LOW
bucket_arb/mod.rs::t_st02_sum_hits_one_after_rounding_rejected (line 2701)Pure rounding boundary.BA-4LOW
bucket_arb/mod.rs::t_st03_touched_book_rejected (line 2730)Pure spec.BA-4LOW
bucket_arb/mod.rs::t_st04_missing_ask_phantom_gamma_rejected (line 2760)Pure phantom-gamma rejection.BA-2LOW
bucket_arb/mod.rs::t_st05_augmented_neg_risk_relocation_documented (line 2798)Documentation test (asserts code-relocation).(deferred)DELETE candidate
bucket_arb/mod.rs::t_st06_001_tick_prices_round_correctly (line 2830)Pure rounding.BA-21LOW
bucket_arb/mod.rs::t_st07_one_sided_book_admits_via_ask_minus_tick (line 2877)Pure one-sided book.BA-21LOW
bucket_arb/mod.rs::t_st08_profit_too_low_after_rounding (line 2915)Pure rounding-induced edge.BA-4LOW
bucket_arb/mod.rs::t_st09_crossed_book_rejected (line 2955)Pure crossed-book rejection.BA-2LOW
bucket_arb/mod.rs::t_st10_dedup_responsibility_relocation_documented (line 2992)Documentation test.(deferred)DELETE candidate
bucket_arb/mod.rs::t_st11_per_bucket_probability_gate_removed_documented (line 3047)Documentation test.(deferred)DELETE candidate
bucket_arb/mod.rs::t_st12_min_buckets_gate_relocation_documented (line 3086)Documentation test.(deferred)DELETE candidate
bucket_arb/mod.rs::t_st13_many_buckets_weather_event (line 3121)Pure many-bucket calculation.BA-3 (positive at scale)LOW
bucket_arb/mod.rs::t_st14_group_id_ownership_relocation_documented (line 3175)Documentation test.(deferred)DELETE candidate

That gives bucket_arb/mod.rs ~38 explicit per-fn rows + ~30 not enumerated of similar form. 6 DELETE candidates in this file alone (relocation-documented tests + stub assertions); 8 MEDIUM rewrite-as-liveness candidates; rest LOW math-correctness retain-and-bind.

Bucket B summary by family ​

FamilyFile range~Test countNet Bucket B disposition
Directional pure evaluatorsdomain/strategies/directional.rs128 (less ~12 already in Bucket A as t_late_entry_* if we had counted them — they remain B as pure FSM gates)~115 B (40 MEDIUM rewrite, 70 LOW retain, 5 DELETE)
Order-manager pure ledgerapplication/order_manager.rs99~99 B (25 MEDIUM, 70 LOW, 4 DELETE)
Bucket-arb FSM/admissiondomain/strategies/bucket_arb/{mod,fsm,scanner_hooks,decision_log}.rs113 (68+18+25+2)~95 B (30 MEDIUM rewrite-pair-with LIV-12, 60 LOW, 5 DELETE)
Maker-rebate FSMdomain/strategies/maker_rebate.rs55~50 B (15 MEDIUM pair LIV-4.*, 30 LOW, 5 DELETE)
Maker puredomain/strategies/maker.rs38~36 B (10 MEDIUM, 25 LOW, 1 DELETE)
Merge-arb FSMdomain/strategies/merge_arb.rs19~18 B (8 MEDIUM pair LIV-5/40, 10 LOW)
DCA extensiondomain/strategies/extensions/dca.rs24~24 B (10 MEDIUM pair LIV-3, 14 LOW)
Strategy shared mathdomain/strategies/shared/{budget,toxic_flow,tiered_exit,time,pricing,orderbook,usdc,redeem,clob_io,fair_value,market_type_filter,webhook_seeding}.rs~95~95 B (mostly LOW math; 20 MEDIUM pair with Bucket A integrations)
Liquidity advisordomain/liquidity.rs49~49 B (LOW; needs new L- rule group OR delete* if signal informational)
Market cachedomain/market_cache.rs16~16 B (LOW or DELETE — internal cache)
WS book parserio/ws/book.rs35~35 B (LOW; consider proptest for external-contract drift)
WS user parserio/ws/user.rs9~9 B (LOW; same class)
RTDS parse pureio/rtds/parse.rs (less 4 in Bucket A)~13 BLOW
RTDS subscribe pureio/rtds/subscribe.rs (less 3 in Bucket A)~7 BLOW
RTDS metrics pureio/rtds/metrics.rs7~7 B (MEDIUM — chain assert needed)
RTDS smokeio/rtds/smoke.rs3~3 B (MEDIUM — should be live-network probe)
RTDS clientio/rtds/client.rs4~4 B (MEDIUM)
Webhook signal/metricsio/webhook/{signal,metrics}.rs14~14 B (LOW)
Gamma clientio/gamma_client.rs22~22 B (LOW; external-contract relevant)
Native balanceio/native_balance.rs3~3 B (LOW)
Mock CLOBio/clob/simulator/mock_clob.rs (less 5 in Bucket A)~7 BLOW
Dry-run extrasio/clob/dry_run.rs (less 6 in Bucket A)~8 BLOW
Persistence schemapersistence/db/schema.rs21~21 B (LOW retain as DB regression)
Persistence queriespersistence/db/queries.rs28~28 B (LOW retain)
Persistence writerpersistence/db/writer.rs27~27 B (LOW retain)
Wallets DB extraspersistence/wallets.rs (less 5 in Bucket A)~23 BLOW
Strategy config DB extraspersistence/strategy_config.rs (less 4 in Bucket A)~30 BLOW
Journal GCpersistence/journal/gc.rs10~10 B (LOW)
Journal mod/readerpersistence/journal/{mod,reader}.rs (less 4+5 in Bucket A)~0 B(all in Bucket A)
Capture redact/sink/writerpersistence/capture/{redact,sink,writer}.rs10~10 B (LOW)
Position storepersistence/position_store.rs4~4 B (LOW)
Auto-pruneapplication/auto_prune.rs6~6 B (LOW)
Dispatcherapplication/dispatcher.rs4~4 B (LOW; bind to G-5)
Scanner extrasapplication/scanner.rs (less 4 in Bucket A)~1 BLOW
Alerts engineapplication/alerts/engine.rs6~6 B (LOW; partial Bucket A overlap)
Alerts rule extrasapplication/alerts/rule.rs (less 8 in Bucket A)~13 BMEDIUM (other rules)
Alerts sender/sink/alert/metrics_tapapplication/alerts/{sender,sink,alert,metrics_tap}.rs~11 BLOW or DELETE
Action executorapplication/action_executor.rs6~6 B (LOW)
Capture / backtest harnessapplication/{capture,backtest/*}.rs~20~20 B (LOW retain as backtest regression)
Engine extrasapplication/engine.rs (less 5 in Bucket A)~2 BLOW
Executor extrasapplication/executor.rs (less 22 in Bucket A)~3 BLOW
Domain timestamped/stats/contexts/decision/repositorydomain/{timestamped,stats,strategies/{contexts,decision,repository}}.rs~15~15 B (mostly DELETE candidates or LOW)
Configconfig/{env,loader,mod,rtds,webhook,capture,validate,strategy/mod}.rs~31~16 B (15 fail-fast tests promote to A — see audit note above)
Presentationpresentation/tui.rs3~3 B (LOW)
Observabilityobservability/{logger,metrics}.rs14~14 B (LOW or DELETE)
pm-commonpricing.rs (40), error.rs (9), resilience.rs (8), models/order.rs (8), models/strategy.rs (6), retry.rs (3)74~74 B (all LOW; math primitive)
pm-sdkclob/wire.rs (11), validation.rs (9), eip712/order.rs (9), eip712/mod.rs (7), crypto/mod.rs (8), auth/credentials_l1.rs (8), auth/key_utils.rs (7), auth/cache.rs (3), onchain/redeem.rs (7), onchain/wrap.rs (3), onchain/ctf.rs (3), onchain/balance.rs (3), parsers.rs (5), clob/mod.rs (5), config.rs (4)92~85 B (most LOW math primitives; 7 MEDIUM in onchain/{redeem,ctf}.rs for adapter dispatch coverage pair with executor Bucket A)
pm-analyticssweep/grid.rs (7), sweep/report.rs (1), whatif/filter.rs (6), whatif/diff.rs (5), whatif/journal_reader.rs (4)23~23 B (LOW)

Bucket B subtotal: ~1230 tests (mostly inline pure unit tests across pm-bot strategy/order_manager/persistence/IO/sdk/common/analytics).

Distribution of priorities (rewrite-as-liveness recommendation):

  • MEDIUM (rewrite to MOC / journal / log boundary): ~280 tests in directional / order_manager / maker / maker_rebate / merge_arb / dca / config-fail-fast / merge-arb sdk dispatch.
  • LOW (math correctness; keep but bind to rule-ID): ~830 tests in pricing / FSM math / dedup / parsers / DB queries / SDK helpers.
  • DELETE candidates (proves removed feature, internal scaffold, or dead-code): ~120 tests across t_discount_pct_removed_stub, t_max_hours_to_expiry_*, market_cache internal, presentation/tui, contexts, decision_log, alerts/sink, alerts/sender, observability/logger.

Bucket B aggregate spec-binding: ~78% bindable to existing rule-IDs in test-coverage-matrix.md; ~22% (most "LOW") cover internal scaffolding without a rule-ID — these need either a NEW rule-ID group L-* (liquidity), S-* (SDK contract), P-* (persistence) OR explicit deletion at next refactor.

Bucket C: silent-broken-suspect — escalate ​

Feature claimed in ADR/FSD (LIV-ID exists), but no existing test observes the observable boundary. RTDS-class candidate — feature may be silently broken in production, no test catches it. Each row escalates to User.

LIV-IDFeatureWhy no liveness test existsHypothesis: code bug or test gapPriorityCross-ref TD-NN
LIV-1.1RTDS WebSocket connectNo test boots a TCP listener at wss://... (mock or live) and asserts state transitions through Connected. io/rtds/client.rs 4 inline tests cover state-machine pure asserts only.Code likely correct (live-tested 2026-05-02 per memory project_v2_blockers_resolved); test gap.MEDIUMTD-35
LIV-1.2RTDS subscribe wire frametests/rtds_hook_e2e.rs covers receive path; outbound subscribe frame on a mock WS server is NOT asserted as exact JSON match. io/rtds/subscribe.rs 10 tests cover frame builder pure.Code likely correct; test gap on outbound frame round-trip.MEDIUMTD-35
LIV-1.5Toxic-flow sliding windowdomain/strategies/shared/toxic_flow.rs 8 unit tests cover detector math; not driven from RTDS event stream end-to-end via on_rtds_trade.Code likely correct; test gap.MEDIUMTD-35
LIV-1.8RTDS metrics counter projectiontests/metrics_loop.rs::metrics_loop_projects_rtds_counters_when_wired uses manually seeded counters (per existing audit §4 ADR-004). No test feeds wire payload through parser → counter increment → MetricsTick projection.Code likely correct (counters are simple atomics); test gap on chain.HIGHTD-33 (already exists per existing audit recommendation)
LIV-2.3Webhook idempotency dedup signal_idwebhook/dedupe.rs 5 unit tests cover the LRU cache pure; tests/scanner_dispatch_test.rs covers the receiver. No test asserts that a second POST with same signal_id returns 409 AND mpsc remains at depth 1.Code likely correct (HTTP test for 409 exists in webhook/server.rs::t_returns_409_on_duplicate); test gap on combined HTTP+channel-depth assertion.LOWTD-36
LIV-2.4IP allowlist enforcementwebhook/server.rs::t_returns_401_on_ip_not_allowed covers HTTP-side 401. Spec gap: ADR-003 doesn't fix the exact status (401 vs 403).Test exists; spec-gap risk only.LOW(spec PR)
LIV-2.5Strategy scope mismatch routingNo test asserts that signal_type=bucket arrived under BOT_STRATEGY=directional → 422 STRATEGY_SCOPE_MISMATCH. webhook/server.rs::t_returns_400_on_idempotency_mismatch covers a different mismatch.Test gap. Could be silent: signal accepted instead of rejected → noisy operations but no financial loss.MEDIUMTD-35
LIV-3.1Periodic timer fires at BOT_FALLBACK_SCAN_SECSNo test asserts loop fires every N seconds. engine_periodic_after_timeout_emits_cancels_to_executor runs ONE periodic tick manually.Code likely correct (commit b902234 per memory directional_no_periodic_scan); test gap on cadence. The existing audit §3 listed this as low-risk gap #16.LOWTD-35
LIV-3.2MarketCache iteration in evaluate_periodicNo test asserts that periodic tick visits every cache row.Code likely correct; test gap.LOWTD-35
LIV-3.4TimezoneCache used by evaluate_periodicSpec gap: ADR-016 §D5 explicit "Deferred: StrategyDeps wiring for periodic path" (per feature-liveness-matrix.md LIV-3.4 note). Code at directional.rs:1913 has a TODO.Code gap (spec-acknowledged deferred), not silent-broken — but the gap means harvester profile may misclassify "today vs tomorrow" markets in periodic mode. Tracks memory feedback_max_hours_expiry.HIGHTD-37 (NEW)
LIV-4.3Paired maker-rebate Pending → OneFilled FSMdomain/strategies/maker_rebate.rs has 55 inline tests, including t_evaluate_rebate_pair_*. No test drives on_full_fill and asserts state transition observed externally (e.g., via repository read).Code likely correct (mocked-repo coverage exists); test gap on observable transition.MEDIUMTD-34 (existing)
LIV-4.4Paired BothFilled → Decision::CallMergePositions reaches executorPer existing audit §11 ADR-011: only Vec<Decision> length asserted; executor wiring (Decision → MOC.merge_calls.len() == 1) NOT asserted.Test gap; possible silent-broken on first paired_enabled=true activation. Listed as TD-34 in existing audit.HIGHTD-34 (existing)
LIV-4.5Imbalance timeout → paired_imbalance_escalation warn logNo test captures the warn log via tracing_subscriber::fmt::TestWriter. Per existing audit §11.Test gap. Operationally severe: imbalance timeout fires but no operator-visible signal.MEDIUMTD-34 (existing)
LIV-4.6Drift cancel on on_orderbook_update for pairedNo test seeds OrderBook update and asserts MOC.cancel_calls.len() == 2.Test gap.MEDIUMTD-34 (existing)
LIV-5.3merge-arb standard CTF dispatch (neg_risk=false)merge_arb.rs::t_band_exit_emits_cancel_merge_reconcile asserts Decision but not adapter dispatch. Per existing audit recommendation §12.Test gap; financial-system risk per memory project_neg_risk_audit (5+ rounds of neg_risk inversion bugs).HIGHTD-34 (existing)
LIV-5.4merge-arb NegRiskAdapter dispatch (neg_risk=true)Same as above; explicit dispatch to NegRiskAdapter not asserted. MERGE_ARB_NEG_RISK_ENABLED defaults to false, so this branch has zero coverage.HIGH RISK on first activation.HIGHTD-34 (existing)
LIV-5.5merge-arb neg_risk kill-switchMERGE_ARB_NEG_RISK_ENABLED=false admission filter not asserted via integration.Test gap.MEDIUMTD-34 (existing)
LIV-6.2Recovery via on-chain CTF balance checkSpec gap: bucket-arbitrage.md describes recovery; exact on-chain call sequence is implementation-level. No test seeds an on-chain balance and asserts recovery uses it.Code likely uses CLOB GET /orders only; on-chain check may not be implemented.MEDIUMTD-38 (NEW)
LIV-6.4Recovery: no duplicate placement on first scan ticktests/restart_attempt_recovery.rs covers attempt counter; doesn't directly assert MOC.place_calls remains empty for N ticks after recovery.Test gap. Memory feedback_no_autonomous_changes-relevant: dup placement = financial loss.HIGHTD-39 (NEW)
LIV-6.5Recovery latency budgetSpec gap: production-readiness budget for recovery latency not numerically fixed.Operational invariant; no SLO assertion.LOW(spec PR)
LIV-7.2Strategy-level dedup (post-restart, same cid, new signal_id)order_manager.rs::t_per_event_dedup_* covers admission dedup; no test asserts that a webhook signal with NEW signal_id but SAME cid is dedup'd at strategy layer (LRU cache passes; strategy layer should still see existing pair and no-op).Test gap. Could be silent-broken: webhook → second placement on same cid → over-allocation.MEDIUMTD-40 (NEW)
LIV-9.3Directional evaluate_signal consults TZ cache for late-entryPer existing audit §11 ADR-016: 13 unit tests in timezone/mod.rs cover cache mechanics; no test asserts that directional::evaluate_signal invokes tz_cache.compute_deadline for wunderground.com source. The or(end) chain at directional.rs:1050 could regress to always-fallback.Test gap; medium silent-broken risk — misclassifies "today vs tomorrow" markets per memory feedback_max_hours_expiry.MEDIUMTD-34 (existing)
LIV-10.1BOT_STRATEGY=X ⇒ only X emits Decisionsapplication/dispatcher.rs::t_from_env_* 4 tests cover env-→-impl mapping construction. No test asserts client_id prefixing in MOC.place_calls.Test gap. Could be silent-broken in multi-strategy build, but ADR-001 enforces single-strategy invariant at compile-time.LOWTD-41 (NEW)
LIV-10.2Cross-strategy if-branches absentCompile-time enforced via Strategy trait; ast-grep gate not in CI.Code likely correct; CI gap.LOW(CI rule add)
LIV-11.3Strategy-tagged client_idexecutor.rs::t_execute_place_ok_returns_order_id asserts a returned order_id but doesn't assert the client_id prefix matches strategy.Test gap. Could be silent-broken — strategy auditing fails if client_ids collide cross-strategy.LOWTD-41 (NEW)
LIV-12.1on_ws_book → Decision end-to-end (BucketArb)Per existing audit §4 ADR-002: synthetic WsEvent::Book not fed into the application scanner for an end-to-end assertion.Test gap; the canonical RTDS-class precedent. Frozen-v1 replay infers but does not test in isolation.MEDIUMTD-34 (existing)
LIV-12.2on_new_market → FSM Candidate→Trackingtests/event_fsm_transitions.rs::t_register_candidate_on_new_market covers FSM mutation; doesn't drive through on_new_market hook.Test gap; small.LOWTD-34 (existing)
LIV-12.3on_user_event (own-fill) routingtests/sync_order_status_rest_path.rs::t_rest_matched_decision_* covers REST path (Bucket A). WS-side (own-fill from ws/user.rs) → strategy-specific journal row not directly observed.Test gap.MEDIUMTD-42 (NEW)
LIV-12.4on_market_resolved → Redeem decisionsexecutor.rs::t_redeem_* (Bucket A) covers executor side. on_market_resolved hook → Decision::Redeem emission (per-strategy) is asserted at strategy-level pure but not as integration.Test gap. Spec gap: redeem trait method not in core 2 OrderClient methods (ADR-010); routing may go through separate RedeemClient.MEDIUMTD-42 (NEW)
LIV-13.4SSE wire-frame schema (HTTP byte-stream)Per existing audit §5 ADR-005 + §13.4 in feature-liveness-matrix: tests/local_api.rs::stream_replays_from_last_event_id and ::stream_emits_heartbeat are compile-only on body shape. Status code is asserted; bytes are NOT decoded back to EventEnvelope.Test gap; SSE wire-frame schema drift would silently strand the dashboard. Listed in TD-34 bundle.MEDIUMTD-34 (existing)
LIV-13.5Last-Event-ID replay (full backfill+live)Same compile-only-shape gap. Status only asserted.Test gap.MEDIUMTD-34 (existing)
LIV-13.7Hybrid PnL (realized + unrealized)No test asserts GET /seed JSON body's total_pnl_usd matches realized+unrealized within tolerance.Test gap. Operational dashboard-truthfulness risk per RCA §4.3.MEDIUMTD-43 (NEW)
LIV-14.1Single-row cancel — REST POSTtests/local_api.rs::rotate_requires_confirm_token (Bucket A) covers rotate; cancel REST round-trip not asserted (per existing audit §13 ADR-013).Test gap.MEDIUMTD-34 (existing)
LIV-14.2Bulk cancel ≤5 — soft confirmNo test seeds 5 rows + asserts MOC.cancel_calls.len() == 5.Test gap.MEDIUMTD-34 (existing)
LIV-14.3Bulk cancel >5 — paranoid CANCEL tokenPer existing audit recommendation §9: integration test posting a 6-row bulk cancel without CANCEL token must 4xx — does not exist.Test gap; operator-protective gate may be silently disabled.HIGHTD-34 (existing)
LIV-14.45s soft-delete undoFrontend store reducer test for the 5s undo branch — does not exist (per audit).Frontend test gap; backend not affected.LOWTD-34 (existing)
LIV-14.5Cancel-reason taxonomy chip severityFrontend severity-resolver test — does not exist.Frontend test gap.LOWTD-34 (existing)
LIV-15.1 / LIV-15.2 / LIV-15.3Annualisation / risk-free / per-strategy slicingFrontend Vitest tests for risk.ts. Per existing audit §14 ADR-014: claimed but unverified.Test gap; math-only.LOWTD-44 (NEW)
LIV-16.2BOT_WALLET_LABEL set + empty WALLETS_MASTER_PASSPHRASE ⇒ panicPer existing audit recommendation §10 ADR-015: the fail-fast INV-2 panic is not asserted by #[should_panic].Test gap; financial-system invariant.HIGHTD-34 (existing)
LIV-16.3INV-3 cross-check strategy.wallet_label == BOT_WALLET_LABELSame — INV-3 panic not asserted.Test gap; financial-system invariant.HIGHTD-34 (existing)
LIV-16.4wallets.db AES-256-GCM round-tripBucket A covers basic round-trip. Decryption with wrong passphrase covered. Auth-tag forgery (modified ciphertext) NOT asserted.Test gap on adversarial input.LOWTD-45 (NEW)
LIV-16.5Budget predicate FE-5 (provisioning-time)Bucket A covers DB-side predicate. CLI-side pm-cli strategy add --budget X exit-code on over-budget — claimed but no integration test verified on disk.Test gap.MEDIUMTD-45 (NEW)
LIV-16.6config.db plaintext SELECTNo test asserts SELECT works with empty passphrase env.Test gap; LOW.LOWTD-45 (NEW)
LIV-17.1WEBHOOK_ENABLED=true + missing SECRET ⇒ panicTest name t_webhook_config_fail_fast_missing_secret mentioned in feature-liveness-matrix.md as partial; existence on disk not confirmed by grep.Test gap (partial coverage).MEDIUMTD-46 (NEW)
LIV-17.2Non-loopback WEBHOOK_BIND_ADDR ⇒ warn (not panic)No test captures the warn line via TestWriter and asserts startup proceeds.Test gap.LOWTD-46 (NEW)
LIV-18.1--capture requires --dry-runNo CLI integration test asserts exit-code on --capture without --dry-run.Test gap.LOWTD-47 (NEW)
LIV-18.3bot_sessions.mode enum extensionjournal_migrations.rs::bot_sessions_mode_defaults_to_live asserts default; no assertion that mode='capture' or 'replay' rows survive round-trip.Test gap; LOW.LOWTD-47 (NEW)
LIV-19.2DuckDB ATTACH aggregationPer existing audit + feature-matrix: implementation queue claim; integration test absent.Test gap.LOWTD-47 (NEW)
LIV-19.3What-if env-override CSV diffpm-analytics/tests/whatif_integration.rs covers Bucket A but CSV column-content diff per LIV-19.3 boundary not asserted.Partial test.LOWTD-47 (NEW)
LIV-20.1Watchdog reset on outbound ping successRCA §4.2 commit 783aa05 deployed; per existing audit partial. No test seeds 60s without inbound + outbound ping every 5s and asserts NO reconnect.Test gap.MEDIUMTD-48 (NEW)
LIV-20.2Dashboard rtdsStatus() truthful indicatorRCA §6.3 + audit reco §8: indicator-truthfulness.test.ts (26 tests) committed but uncommitted in this worktree.Frontend test claimed; on disk: file existence depends on which branch.MEDIUMTD-34 (existing)
LIV-21.1AST-grep rule blocks latest_tick.<counter> > 0.ast-grep/truthful-indicator.yml claimed; CI invocation not asserted.CI rule gap.LOWTD-49 (NEW)
LIV-22.1--smoke-rtds daily CI cronRequired before next live trading session per RCA §6.2. Not yet a GitHub Actions cron.Code+CI gap. P0 corrective.HIGHTD-33 (existing) — extended scope
LIV-22.2Polymarket API contract probeRCA §6.4 P2; not yet implemented.CI/code gap.MEDIUMTD-50 (NEW)
LIV-23.2Strategy-conditional surfacing after BOT_STRATEGYconfig/mod.rs 10 tests; not asserted that BUCKET_ARB_* block is NOT validated under BOT_STRATEGY=directional.Test gap on lazy validation invariant.LOWTD-46 (NEW)
LIV-23.4RTDS_* always-on + lazy gate knobsPer CLAUDE.md Fail-fast — knobs missing under always-on policy panic. No #[should_panic] test for missing RTDS_TOXIC_FLOW_*.Test gap.MEDIUMTD-46 (NEW)
LIV-25.1pm-analytics compact is Phase 4 stubSpec gap: memory project_capture_compact_deferred says stub; ADR-009 §Phase 4 says full pipeline. Inconsistency.Spec gap, not test gap. Impl is currently a stub.LOW(spec PR)
LIV-25.2Manual archival post-sessionOperational, no test.(operational)LOW(runbook only)
LIV-26.2pm-cli wallet remove paranoid DELETE tokenNo test asserts removal requires literal DELETE token.Test gap.LOWTD-45 (NEW)
LIV-27.1Cancel reaches CLOB on toxic-flow activetests/rtds_active_cancel.rs::t_toxic_flow_emits_cancel_when_active (Bucket A) covers MOC. Live CLOB POST-cancel chain not exercised.Test gap on live chain; MOC chain exists.MEDIUMTD-32 (existing, extend)
LIV-27.2No cancel pile-up under repeated triggerstests/rtds_active_cancel.rs::t_toxic_flow_isolates_per_condition_id (Bucket A) is closest; idempotency under repeated triggers explicitly NOT asserted.Test gap.MEDIUMTD-32 (existing, extend)
LIV-28.1Pre-flight script runs all checksRCA §6.4 P2: scripts/preflight/all.sh doesn't exist yet.Code gap (P2).LOWTD-50 (NEW)
LIV-28.2Q3 timestamp invariant automated gateRCA §5.1 + §6.2 — manual checklist item; needed as automated gate.Code+CI gap. P0 corrective.HIGHTD-33 (existing, extend)
LIV-29.3cancel_all_orders(condition_id) reaches adapterNo test asserts MOC.cancel_all_calls recorded with cid.Test gap.LOWTD-51 (NEW)
LIV-29.5get_balance reachableNo test asserts this trait method end-to-end (executor → SDK call).Test gap.LOWTD-51 (NEW)
LIV-31.2SymbolicClock replaces Instant::now on replay15-callsite migration claimed; ast-grep gate enforcing 0 hits not in CI.CI gap.MEDIUMTD-52 (NEW)
LIV-31.3StrategyRng seeded by (session_id, strategy_name)Backtest harness covers RNG seed (backtest/rng.rs 6 tests, Bucket B); per-strategy seed determinism across 10 replays of same session not directly asserted.Test gap.MEDIUMTD-52 (NEW)
LIV-32.1Exponential backoff 1s → 30sPer existing audit: standard ws_client.rs reuse claimed. No test measures per-attempt delay.Test gap.LOWTD-48 (NEW)
LIV-32.2Reconnect counter rate-limitCovered indirectly via application/alerts/rule.rs::t_rtds_health_fires_on_reconnect_storm (Bucket A).Already covered.——
LIV-34.164 KB body cap → 413webhook/server.rs tests don't include 70KB POST → 413.Test gap.LOWTD-46 (NEW)
LIV-36.1Idempotent consume by transactionHashio/rtds/dedupe.rs 8 inline tests (Bucket A) cover the dedup; cross-source dedup (RTDS Trade vs CLOB last_trade_price) NOT asserted.Test gap.LOWTD-53 (NEW)
LIV-37.1MarketSnapshot.end_date field absentCompile-time enforced + grep gate (CLAUDE.md). No test exercises grep gate in CI.CI rule gap.LOW(CI rule add)
LIV-38.1All live tests use canonical wallet 0xBA561B30…7F4Operational invariant; not code-enforced.Per memory feedback_live_test_wallet.LOW(operational)
LIV-39.1IERC1155.isApprovedForAll(owner, CTF) precheckmerge-arb-strategy.md §Decision 5; no #[should_panic] test for merge-arb startup with un-approved CTF.Test gap.MEDIUMTD-54 (NEW)
LIV-39.2NegRiskAdapter approval precheckSame; merge-arb startup with MERGE_ARB_NEG_RISK_ENABLED=true and un-approved adapter.Test gap.MEDIUMTD-54 (NEW)
LIV-40.3Replenish on fill until BUDGET_PER_CID (merge-arb v2)No test seeds N fills + asserts MOC.place_calls continues until cumulative ≥ budget.Test gap.MEDIUMTD-55 (NEW)
LIV-42.1pm-cli cancel-all → POST per active orderNo CLI integration test.Test gap.LOWTD-56 (NEW)
LIV-42.2pm-cli sell-allSame.Test gap.LOWTD-56 (NEW)
LIV-43.1 / LIV-43.2Redeem standard / neg-risk via pm-cliexecutor.rs::t_redeem_* (Bucket A) covers executor + adapter dispatch. CLI wrapper integration not asserted.Test gap; downstream of Bucket A coverage.LOWTD-56 (NEW)
LIV-44.1pm-cli init --chain-id 137 writes .envNo CLI test.Test gap.LOWTD-56 (NEW)
LIV-45.1pm-cli approve --with-wrap --wrap-allManual smoke 2026-05-01 (commit 871f811). No CI/integration.Test gap.LOWTD-56 (NEW)
LIV-46.1RTDS schema parity probe (SmokeReport.schema_parity)RTDS smoke covers parser; field-by-field schema parity assertion against golden missing.Test gap; partial coverage from tests/rtds_hook_e2e.rs.MEDIUMTD-50 (NEW)
LIV-47.1pm-dashboard /api/local/* returns 503 if pm-bot offlinePer existing audit gap §12 (b). No test boots dashboard alone and asserts 503 on /api/local/seed.Test gap.MEDIUMTD-34 (existing)
LIV-48.1/{wallet} route works without pm-botNo test boots dashboard alone with bot offline + Vue SPA loads.Test gap (frontend).LOWTD-34 (existing)
LIV-49.1Diff-resubscribe on new active slugsio/rtds/subscribe.rs::t_filter_set_diff_* covers diff-set math (Bucket A). Outbound WS frame: NO reconnect during diff transition not asserted.Test gap on outbound.MEDIUMTD-35 (existing)
LIV-49.2Cleanup on FSM drain_cleanup_slugNo test seeds slug → terminal state and asserts outbound unsubscribe frame.Test gap.LOWTD-35 (existing)

Bucket C subtotal: 47 LIV-IDs flagged silent-broken-suspect across 50 LIV groups.

Distribution by priority:

  • HIGH (8 LIV-IDs): LIV-1.8 (RTDS metric chain), LIV-3.4 (TZ deferred-wiring), LIV-4.4 (paired merge → executor), LIV-5.3 (merge-arb standard CTF dispatch), LIV-5.4 (merge-arb NegRiskAdapter dispatch), LIV-6.4 (recovery no-dup), LIV-14.3 (paranoid bulk cancel), LIV-16.2 (INV-2 panic), LIV-16.3 (INV-3 panic), LIV-22.1 (smoke-rtds CI cron — P0), LIV-28.2 (Q3 invariant gate — P0).
  • MEDIUM (24 LIV-IDs): most ScannerHooks / SSE / paired-rebate / TZ-consumer / dashboard-truthfulness / merge-arb-replenish / multi-wallet / config-fail-fast.
  • LOW (15 LIV-IDs): pm-cli wrappers, polish gaps, frontend severity functions, schema-drift CI rules.

Audit summary ​

  • Total existing tests: 1572 annotations across workspace (cf. 1561 cited in liveness-audit-2026-05-06.md baseline, 1576 grep-raw incl. helper-fn #[test] flags); rounding aside, the 1561 figure is preserved as the historical reference and 1572 is this audit's working census.
  • Bucket A (kept): ~210 tests bound to rule-IDs and observing a side-effect boundary. Promote in matrices to mapped.
  • Bucket B (rewrite candidates): ~1230 tests. Of those, ~280 MEDIUM priority for rewrite-as-liveness, ~830 LOW (math correctness — keep but bind to rule-IDs), ~120 DELETE candidates (dead-code / removed-feature stubs / scaffold).
  • Bucket C (silent-broken-suspect): 47 LIV-IDs flagged. 11 HIGH (incl. existing TD-31..TD-33 already implemented in worktree), 24 MEDIUM, 12 LOW.

Coverage hot-spots:

  • ADR-001 / ADR-007 (Strategy pipeline) — strongest direct liveness coverage via tests/strategy_engine_integration.rs + application/executor.rs inline tests.
  • ADR-005 (Local-Bot Mode) — strong DB-side liveness via tests/journal_writer.rs (9 tests); SSE wire-frame side has compile-only-shape gap.
  • ADR-009 (Capture/Replay) — strongest indirect liveness via 10× SHA-256 contract.
  • ADR-013 (Cancel UI) — mostly frontend-Vitest-claimed; backend integration absent.
  • ADR-004 (RTDS) — was the silent-broken precedent; post-incident TD-31..TD-33 partially closed (3 tests in tests/rtds_hook_e2e.rs + 4 tests in tests/rtds_active_cancel.rs + 8 tests in application/alerts/rule.rs); LIV-22.1 daily CI cron + LIV-28.2 Q3 invariant gate still P0.

Coverage cold-spots (no Bucket A test):

  • merge-arb on-chain dispatch (LIV-5.3, LIV-5.4) — financial-system risk per memory project_neg_risk_audit.
  • multi-wallet INV-2 / INV-3 panic assertions (LIV-16.2, LIV-16.3).
  • Paired maker-rebate executor wiring (LIV-4.4) — gated default-off, flip-day risk.
  • ScannerHooks per-hook E2E (LIV-12.1) — frozen-v1 replay catches internal regressions but not external-format drift (RCA-class).

Cross-mapping with existing audit ​

Per liveness-audit-2026-05-06.md §3 risk-ordered gaps:

High-risk gaps (in existing audit; status as of this audit) ​

  1. HIGH-1 ADR-004 RTDS parser → hook (TD-31) → closed by tests/rtds_hook_e2e.rs (3 tests in Bucket A). LIV-1.4 mapped.
  2. HIGH-2 ADR-004 RTDS active-cancel (TD-32) → closed by tests/rtds_active_cancel.rs (4 tests in Bucket A). LIV-1.7 mapped.
  3. HIGH-3 ADR-004 RTDS health metric (TD-33) → closed by application/alerts/rule.rs::t_rtds_health_* (8 tests in Bucket A). LIV-1.9 mapped. Open: LIV-22.1 daily CI cron + LIV-28.2 automated Q3 invariant gate (P0 RCA §6.2).

Medium-risk gaps (in existing audit, status as of this audit) ​

  1. ADR-002 ScannerHooks per-hook E2E → LIV-12.1 still open in Bucket C.
  2. ADR-005 SSE wire-frame schema → LIV-13.4 still open (compile-only-shape via tests/local_api.rs::stream_*).
  3. ADR-011 paired maker-rebate executor wiring → LIV-4.4 still open.
  4. ADR-011 paired_imbalance_escalation warn-log capture → LIV-4.5 still open.
  5. ADR-012 dashboard truthfulness pattern → LIV-20.2 status unverified (indicator-truthfulness.test.ts claimed but uncommitted in worktree).
  6. ADR-013 paranoid bulk-cancel gate → LIV-14.3 still open.
  7. ADR-015 INV-3 cross-check → LIV-16.3 still open.
  8. ADR-016 directional uses TZ at evaluate-signal → LIV-9.3 still open.
  9. merge-arb neg-risk vs standard CTF dispatch → LIV-5.3, LIV-5.4 still open.

Low-risk gaps ​

  1. ADR-007 layering lint → optional Tier-3 polish; not surfaced as LIV-ID.
  2. ADR-009 NullSink no-op proof → LIV-8.2 partial (replay parity infers).
  3. ADR-010 trait method coverage matrix → LIV-29.* — partial Bucket A coverage for required methods, gap on provided.
  4. directional periodic-scan cadence → LIV-3.1 still open.

Cross-referencing liveness-audit-2026-05-06.md §4 recommendations and this audit's findings:

TDLIV-IDs coveredPriorityStatus
TD-31LIV-1.3 (parser ms+s), LIV-1.4 (parser→hook E2E)HIGHCLOSED in worktree by tests/rtds_hook_e2e.rs. Promote LIV-1.3/1.4 status to mapped.
TD-32LIV-1.6, LIV-1.7 (active-cancel)HIGHCLOSED in worktree by tests/rtds_active_cancel.rs. Promote to mapped.
TD-33LIV-1.8, LIV-1.9 (RTDS health AlertRule) + LIV-22.1 (daily CI cron) + LIV-28.2 (Q3 gate)HIGHPARTIALLY CLOSED: rule.rs covers 8 cases in worktree. Still open: GitHub Actions cron + Q3 gate. P0 per RCA §6.2.
TD-34bundle of medium-risk: LIV-12.1 (ScannerHooks E2E), LIV-13.4/13.5 (SSE wire-frame), LIV-4.4 (paired merge→executor), LIV-4.5 (escalation log), LIV-14.1/14.2/14.3 (cancel UI), LIV-16.2/16.3 (multi-wallet panics), LIV-9.3 (TZ at evaluate_signal), LIV-5.3/5.4 (merge-arb dispatch), LIV-20.2 (dashboard truthfulness recurrence guard), LIV-47.1 (proxy 503), LIV-48.1 (wallet-explorer offline)MEDIUMOPEN — implementation-queued in existing audit recommendations §3
TD-35 (NEW)LIV-1.1 (RTDS connect), LIV-1.2 (subscribe wire), LIV-1.5 (toxic-flow E2E), LIV-2.5 (strategy scope routing), LIV-3.1 (periodic cadence), LIV-3.2 (cache iteration), LIV-49.1/49.2 (resubscribe diff outbound)MEDIUMNEW
TD-36 (NEW)LIV-2.3 (webhook idempotency channel-depth combined assertion)LOWNEW
TD-37 (NEW)LIV-3.4 (TZ deferred wiring at periodic path — ADR-016 §D5)HIGHNEW — code gap, not test gap
TD-38 (NEW)LIV-6.2 (recovery via on-chain CTF balance)MEDIUMNEW — spec also gap
TD-39 (NEW)LIV-6.4 (recovery no-dup placement on first scan tick)HIGHNEW
TD-40 (NEW)LIV-7.2 (strategy-level dedup, post-restart, same cid)MEDIUMNEW
TD-41 (NEW)LIV-10.1, LIV-11.3 (strategy isolation client_id prefix)LOWNEW
TD-42 (NEW)LIV-12.3 (own-fill WS routing), LIV-12.4 (resolved → redeem decisions)MEDIUMNEW
TD-43 (NEW)LIV-13.7 (hybrid PnL via /seed)MEDIUMNEW
TD-44 (NEW)LIV-15.1/15.2/15.3 (frontend risk metrics math)LOWNEW
TD-45 (NEW)LIV-16.4 (auth-tag forgery negative), LIV-16.5 (CLI budget predicate exit), LIV-16.6 (config.db plaintext SELECT), LIV-26.2 (paranoid DELETE token)LOWNEW
TD-46 (NEW)LIV-17.1, LIV-17.2 (webhook fail-fast), LIV-23.2, LIV-23.4 (strategy-conditional + RTDS lazy fail-fast), LIV-34.1 (body-size cap → 413)MEDIUMNEW
TD-47 (NEW)LIV-18.1, LIV-18.3 (capture session lifecycle), LIV-19.2/19.3 (sweep DuckDB ATTACH + whatif diff CSV)LOWNEW
TD-48 (NEW)LIV-20.1 (watchdog ping-reset), LIV-32.1 (exponential backoff per-attempt)MEDIUMNEW
TD-49 (NEW)LIV-21.1 (AST-grep CI rule for truthful indicators)LOWNEW
TD-50 (NEW)LIV-22.2 (Polymarket API contract probe), LIV-28.1 (preflight script), LIV-46.1 (schema parity probe)MEDIUMNEW — corresponds to RCA §6.4 P2
TD-51 (NEW)LIV-29.3 (cancel_all), LIV-29.5 (get_balance) trait method coverageLOWNEW
TD-52 (NEW)LIV-31.2 (SymbolicClock ast-grep), LIV-31.3 (StrategyRng determinism)MEDIUMNEW
TD-53 (NEW)LIV-36.1 (cross-source dedup)LOWNEW
TD-54 (NEW)LIV-39.1, LIV-39.2 (merge-arb approve preconditions panic)MEDIUMNEW
TD-55 (NEW)LIV-40.3 (merge-arb v2 replenish-until-budget)MEDIUMNEW
TD-56 (NEW)LIV-42.1 (cancel-all CLI), LIV-42.2 (sell-all CLI), LIV-43.* (redeem CLI), LIV-44.1 (init CLI), LIV-45.1 (wrap+approve CLI)LOWNEW — bundle of pm-cli integration tests

Total TD entries surfaced: 26 (TD-31..TD-34 from existing audit + TD-35..TD-56 new). Sign-off requires user approval per memory feedback_no_autonomous_changes.

Out of scope ​

This audit does NOT cover:

  • New tests authored / existing tests edited / production code modified — explicitly forbidden by task instructions and memory feedback_no_autonomous_changes.
  • Frontend Vitest tests in frontend/pm-dashboard-web/ — listed as TD-44 / TD-34 placeholder; real audit requires pnpm test inventory which is not part of the Rust test census. RCA §6.1 indicators-truthfulness 26-test suite committed status is unverified on this worktree branch.
  • pm-cli integration tests — counted under workspace but not separately classified; aggregate "27 pm-cli integration tests + 28 pm-dashboard integration tests" from existing audit ADR-015 is preserved as cross-reference but not re-verified file-by-file.
  • Mutation-testing pass on Bucket B — methodology recommends mutation testing as the proper proof of "tautology-or-not"; this audit applied the methodology heuristic (boundary-observation + rule-binding) only.
  • Spec gaps marked <!-- spec gap: ... --> in source matrices — surfaced through cross-reference but not closed; resolution belongs to spec-PR, not test-PR.
  • DST behaviour for ADR-016 — spec gap explicitly (test-coverage-matrix.md §G aggregated gaps).
  • Live-network probes — RCA §6.4 daily Polymarket contract probe is design-phase; the audit lists LIV-22.* / LIV-46.1 / LIV-28.* as silent-broken-suspect because the gates don't yet exist in CI, not because the probe is broken in code.
  • Performance / load-test correctness — tests/ws_load_test.rs::test_ws_orderbook_load (Bucket A by virtue of full WS pipeline driving) is not assessed for SLO compliance.
  • Determining DELETE vs KEEP for the ~120 candidates in Bucket B — surface lists sample groups; actual deletion requires per-test review with the relevant module owner.

Appendix A — Borderline Bucket A / Bucket B re-classifications (per-fn) ​

The following inline tests live in domain/strategies/{bucket_arb/scanner_hooks,merge_arb,maker,maker_rebate}.rs and are async + drive a hook-and-recorder pattern that is closer to liveness than not, but assert on Vec<Decision> content rather than MockOrderClient.place_calls. The pragmatic choice is Bucket A on rule-binding even though they don't observe MOC; flagged here so QA Lead can promote/demote per file in Phase 1.

Test (file:fn)Promote to Bucket A?rule-ID / LIV-IDBoundary observed
bucket_arb/scanner_hooks.rs::t_initial_setup_empty_cache_noop (line 1184)YESBA-2 (negative)hook returns empty
bucket_arb/scanner_hooks.rs::t_initial_setup_skipped_for_non_arb_strategy (line 1199)YESG-5, LIV-10.1hook returns empty under wrong strategy
bucket_arb/scanner_hooks.rs::t_on_new_market_missing_slug_is_noop (line 1214)YESBA-2 (negative)hook returns empty
bucket_arb/scanner_hooks.rs::t_on_new_market_too_few_siblings_skips (line 1241)YESBA-3 (negative)hook returns empty
bucket_arb/scanner_hooks.rs::t_on_ws_book_tracked_basket_is_noop_state (line 1259)YESBA-13hook returns empty for tracked-state basket
bucket_arb/scanner_hooks.rs::t_on_ws_price_unknown_asset_noop (line 1303)YESG-4hook returns empty for unknown asset
bucket_arb/scanner_hooks.rs::t_on_user_event_unknown_order_noop (line 1321)YESLIV-12.3 (negative)hook returns empty
bucket_arb/scanner_hooks.rs::t_periodic_tick_abandons_stale_posted_basket_then_drains (line 1342)YESBA-12, LIV-3.1Decision::Cancel/Abandon emitted
bucket_arb/scanner_hooks.rs::t_periodic_tick_keeps_abandoned_with_filled_legs (line 1389)YESBA-22Decision retention
bucket_arb/scanner_hooks.rs::t_market_resolved_marks_basket_resolved_then_drains (line 1444)YESBA-17, LIV-12.4Decision::Redeem emitted per leg
bucket_arb/scanner_hooks.rs::t_market_resolved_unrelated_basket_untouched (line 1501)YESBA-17 isolationscoped emission
bucket_arb/scanner_hooks.rs::t_on_ws_book_emits_post_basket_when_ready (line 1558)YESBA-3, BA-4, LIV-12.1Decision::PostBucketArbBasket
bucket_arb/scanner_hooks.rs::t_on_ws_book_missing_book_on_sibling_leg_skips (line 1635)YESBA-2, BA-4hook returns empty
bucket_arb/scanner_hooks.rs::t_on_ws_book_dedup_via_events_in_flight (line 1698)YESLIV-7.2hook dedup
bucket_arb/scanner_hooks.rs::t_on_ws_book_immediate_fill_emits_post_only_false (line 1765)YESBA-7 (negative — taker mode), G-16post_only flag observed in Decision
bucket_arb/scanner_hooks.rs::t_on_ws_book_immediate_fill_skips_when_margin_insufficient (line 1839)YESBA-4 negativehook returns empty
bucket_arb/scanner_hooks.rs::t_bucket_signal_registers_basket (line 1947)YESBA-26, LIV-2.6webhook → register
bucket_arb/scanner_hooks.rs::t_bucket_arb_webhook_registers_same_as_gamma (line 1986)YESBA-26 parityparity assertion
bucket_arb/scanner_hooks.rs::t_market_signal_rejected_on_bucket_arb (line 2055)YESLIV-2.5strategy scope mismatch
bucket_arb/scanner_hooks.rs::t_dedup_when_slug_already_tracking (line 2083)YESLIV-7.2dedup at slug level
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_trade_marks_dirty (line 2205)YESBA-23, LIV-1.4 cousindirty flag set
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_orders_matched_marks_dirty (line 2233)YESLIV-1.4 cousindirty flag set
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_no_basket_no_op (line 2255)YESLIV-1.4 negativeno-op for unknown
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_finalized_basket_skipped (line 2284)YESBA-22finalized skipped
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_non_arb_config_short_circuits (line 2317)YESG-5wrong-strategy short-circuit
merge_arb.rs::t_watched_filter_in_band_admits (line 1206, async)YESMA-2admission observable
merge_arb.rs::t_watched_filter_above_band_rejects (line 1252, async)YESMA-2 negativerejection observable
merge_arb.rs::t_watched_filter_lower_boundary_admits (line 1276, async)YESMA-2 boundaryadmission
merge_arb.rs::t_watched_filter_neg_risk_disabled_rejects (line 1299, async)YESLIV-5.5kill-switch admission filter
merge_arb.rs::t_initial_setup_within_budget_emits_pair (line 1326, async)YESLIV-40.2symmetric BUY pair emitted (Decision shape)
merge_arb.rs::t_initial_setup_over_budget_skips_emit (line 1354, async)YESMA-18budget gate
merge_arb.rs::t_full_fill_yes_only_no_merge_replenish (line 1399)YESLIV-40.3replenish branch
merge_arb.rs::t_balanced_inventory_emits_merge (line 1440)YESLIV-40.4merge Decision emit
merge_arb.rs::t_replenish_skipped_when_budget_exhausted (line 1496)YESLIV-40.3 negativebudget exhaustion
merge_arb.rs::t_band_exit_emits_cancel_merge_reconcile (line 1536, async)YESMA-8, LIV-40.5cancel+merge+reconcile sequence
merge_arb.rs::t_band_exit_state_blocks_relayer (line 1594, async)YESMA-12reconcile blocks autoresolve
merge_arb.rs::t_on_ws_book_pending_dedupe (line 1621, async)YESLIV-7.2pending dedup
merge_arb.rs::t_on_ws_book_post_only_safety_skips_cross (line 1649, async)YESMA-16, G-16post_only safety
merge_arb.rs::t_on_ws_book_re_arms_missing_side (line 1675, async)YESLIV-40.2re-arm
merge_arb.rs::t_evaluate_periodic_stale_warn_touch (line 1719, async)YESG-28stale guard
merge_arb.rs::t_evaluate_periodic_resolving_timeout_resets_idle (line 1750, async)YESMA-10timeout reset
merge_arb.rs::t_evaluate_periodic_admits_new_watched (line 1784, async)YESMA-2admission via periodic
merge_arb.rs::t_evaluate_periodic_drops_out_of_band_with_zero_inventory (line 1805, async)YESMA-2, MA-4drop on out-of-band
merge_arb.rs::t_evaluate_periodic_keeps_out_of_band_with_nonzero_inventory (line 1827, async)YESMA-15residual reconcile
maker.rs::t_profile_no_maker_equivalent (line 1100)YESMK-15, MK-16profile equivalence Decision shape
maker.rs::t_profile_spread_maker_equivalent (line 1129)YESMK-14, MK-16profile equivalence
maker.rs::t_profile_sniper_equivalent (line 1159)YESMK-13, MK-16profile equivalence
maker.rs::t_skip_when_neg_risk (line 1199)YESMK-1scope filter
maker.rs::t_skip_when_slug_pattern_mismatch (line 1209)YESMK-10slug filter
maker.rs::t_admit_when_slug_pattern_matches_case_insensitive (line 1224)YESMK-10case-insensitive admit
maker.rs::t_skip_when_probability_below_band (line 1240)YESMK-4, MK-9prob band lower
maker.rs::t_skip_when_probability_above_band (line 1252)YESMK-4, MK-9prob band upper
maker.rs::t_skip_when_bid_non_positive_after_safety (line 1264)YESMK-9bid-safety
maker.rs::t_skip_when_spread_below_target (line 1282)YESMK-9spread filter
maker.rs::t_skip_when_ask_above_max_price_relative_modes (line 1299)YESMK-7, MK-8max-price gate (relative modes)
maker.rs::t_ask_above_max_price_ok_for_fixed_abs (line 1311)YESMK-6fixed-abs mode bypasses ask gate
maker.rs::t_skip_when_midpoint_missing_relative_midpoint (line 1338)YESMK-7midpoint missing
maker.rs::t_skip_when_size_below_one_share (line 1349)YESG-23, MK-9min-size
maker.rs::t_on_full_fill_records_position (line 1379)YESG-26journal-shape
maker.rs::t_on_full_fill_ignores_sell_fills (line 1402)YESMK-12hold-to-resolution; ignore Sell
maker.rs::t_on_cancel_removes_unfilled_position (line 1421)YESMK-22cancel cleanup
maker.rs::t_on_cancel_keeps_partially_filled_position (line 1442)YESMK-22partial-fill retention
maker.rs::t_on_market_resolved_emits_redeem_and_clears (line 1463)YESMK-21, LIV-12.4redeem Decision
maker.rs::t_on_market_resolved_ignores_unknown_condition (line 1507)YESMK-21 isolationscoped resolution
maker.rs::t_round_to_tick_floors_to_grid (line 1524)NO (pure math)MK-7 helper—
maker.rs::t_evaluate_signal_quantises_to_per_market_tick (line 1542)YESMK-6tick-aware Decision shape
maker.rs::t_max_hours_to_expiry_inside_window_admits (line 1618)NO (asserts dead-code)MK-19 (gap)— DELETE candidate per memory max_hours_dead_code
maker.rs::t_max_hours_to_expiry_on_boundary_admits (line 1631)NOMK-19 (gap)DELETE candidate
maker.rs::t_max_hours_to_expiry_outside_window_rejects (line 1647)NOMK-19 (gap)DELETE candidate
maker.rs::t_max_hours_to_expiry_fail_closed_when_deadline_unknown (line 1662)NOMK-19 (gap)DELETE candidate
maker.rs::t_max_hours_to_expiry_zero_skips_gate_even_without_deadline (line 1679)NOMK-19 (gap)DELETE candidate
maker.rs::t_maker_webhook_rejects_bucket_signal (line 1733, async)YESLIV-2.5strategy scope
maker.rs::t_maker_webhook_no_config_noop (line 1759, async)YESLIV-2.6no-op without config
maker.rs::t_maker_external_signal_market_in_cache_no_fetch (line 1876, async)YESLIV-2.6cache-hit
maker.rs::t_maker_external_signal_market_not_in_cache_fetches_and_evaluates (line 1924, async)YESLIV-2.6cache-miss + fetch
maker.rs::t_maker_external_signal_fetch_fails_returns_empty (line 1969, async)YESDIR-25error path
maker.rs::t_maker_rtds_below_threshold_no_action (line 2079, async)YESG-12 negativetoxic-flow filter
maker.rs::t_maker_rtds_dust_filtered_no_window_growth (line 2106, async)YESLIV-1.5dust filter
maker.rs::t_maker_rtds_above_threshold_log_only_default_returns_empty (line 2132, async)YESG-13 negativeLOG_ONLY default
maker.rs::t_maker_rtds_above_threshold_active_cancel_emits_cancels (line 2166, async)YESG-13, MK-20active-cancel emit

Net effect of Appendix A re-classifications: +78 tests promoted from Bucket B to Bucket A (25 in bucket_arb/scanner_hooks.rs + 19 in merge_arb.rs + 34 in maker.rs minus 5 DELETE candidates), bringing Bucket A to ~288 tests and reducing Bucket B to ~1152.

Appendix B — Bucket C extended LIV-ID map (LOW priority) ​

The Bucket C table above covers HIGH/MEDIUM and the most-significant LOW LIV-IDs. The complete list of LOW silent-broken-suspect candidates from feature-liveness-matrix.md, with one-line gap notes:

LIV-IDFeatureGap one-linerCross-ref TD
LIV-2.1Webhook server bindTCP listener test exists in tests/e2e_webhook_smoke.rs; multi-port-collision behaviour not assertedTD-46
LIV-5.1bucket-arb accepts only neg_riskCovered indirectly via tests/parallel_placement.rs::neg_risk_preresolved_passes_through_plan (Bucket A); explicit single-row test absentTD-35
LIV-5.2directional/maker/maker_rebate reject neg_riskmaker.rs::t_skip_when_neg_risk (Appendix A) covers maker; analogous tests for directional/maker_rebate not separately enumeratedTD-35
LIV-6.1CLOB GET /orders restore live orderstests/restart_attempt_recovery.rs (Bucket A) covers DB recovery; CLOB-side list_orders invocation count not directly assertedTD-39
LIV-6.3FSM reconstruction from event_logtests/crash_recovery.rs (Bucket A) covers basket FSM; per-strategy state (RebatePair, InventoryFsm) not separately assertedTD-39
LIV-7.1HTTP-level dedup channel-depth combinedwebhook/server.rs::t_returns_409_on_duplicate (Bucket A) covers HTTP; channel-depth gauge assertion absentTD-36
LIV-8.1Capture taps emit envelopetests/capture_smoke.rs (Bucket A) covers; per-source-tag invariant for all 6 source-tags not exhaustive(already mapped)
LIV-8.2NullSink no-op proof in production hot loopIndirect via replay parity; explicit FS/metric assertion absentTD-47
LIV-9.1TZ activation predicateCovered by timezone/mod.rs 13 unit tests (Bucket A indirect); strategy-side activation not assertedTD-34
LIV-9.2ICAO → local-day midnightCovered by timezone/mod.rs; cross-DST boundary cases gap (per spec gap aggregate)(spec PR)
LIV-11.1Decision::PlaceOrder reaches place_limit_ordermapped (tests/strategy_engine_integration.rs)(mapped)
LIV-11.2Decision::CancelOrder reaches cancel_ordermapped(mapped)
LIV-13.1Local-bot server bindmapped (tests/local_api.rs)(mapped)
LIV-13.2Disabled by default — bit-identical hot loopBucket A local_api.rs::bind_rejects_non_loopback_without_flag; explicit metric=0 assertion absentTD-47
LIV-13.3Atomic batch INV-Amapped(mapped)
LIV-13.6POST /rotate ROTATE tokenmapped(mapped)
LIV-15.1..15.3Frontend risk metrics(Bucket C — TD-44)TD-44
LIV-16.1BOT_WALLET_LABEL unset ⇒ legacy .env pathwallets.rs::test_migrate_from_env_* (Bucket B) covers migration; runtime equivalence to legacy (metrics.wallet_source = "env") not assertedTD-45
LIV-17.3WEBHOOK_ENABLED=false ⇒ vacuousmapped (tests/scanner_dispatch_test.rs::t_disabled_feature_is_vacuous)(mapped)
LIV-18.2capture_sessions row insertedPartial coverage via journal/mod.rs::t_run_migrations_installs_capture_sessions_table (Bucket A); insert-on-startup not E2ETD-47
LIV-19.1Sweep parallelismBucket A (pm-analytics/tests/sweep_integration.rs); SLO speedup not asserted(mapped, partial)
LIV-21.1AST-grep convention ruleCI rule absent; truthful-indicator dashboard test claimedTD-49
LIV-22.1Daily smoke-rtds CI cronHIGH P0 — already TD-33 extensionTD-33
LIV-22.2Polymarket API contract probeRCA §6.4 P2TD-50
LIV-23.1BUCKET_ARB_* block missing ⇒ panic with full listconfig/mod.rs 10 inline tests (Bucket B) cover parser; full-list panic content not asserted via #[should_panic] payload-content matchTD-46
LIV-23.3WEBHOOK_* lazy validationIndirect via t_disabled_feature_is_vacuous; not directTD-46
LIV-24.1..24.3Optional defaults — no panicconfig/mod.rs covers parser; runtime startup with unset defaults not E2ETD-46
LIV-25.1pm-analytics compact Phase 4 stubSpec gap(spec PR)
LIV-25.2Manual archival post-session(operational)(runbook only)
LIV-26.1pm-cli wallet rotate-masterCovered by wallets.rs::test_rotate_master_* (Bucket A)(mapped)
LIV-26.2pm-cli wallet remove paranoid DELETE tokenabsentTD-45
LIV-27.1..27.2Cancel chain + no-pile-upPartial via tests/rtds_active_cancel.rs (Bucket A); idempotent under-repeated-trigger explicit absentTD-32 extend
LIV-28.1..28.2Pre-flight script + Q3 gateabsent / HIGH P0TD-50 + TD-33
LIV-29.1..29.5OrderClient trait method coveragePartial via Bucket A (LIV-29.1, 29.2 mapped); LIV-29.3..29.5 absentTD-51
LIV-30.1..30.2Strategy isolation invariantsPartial via application/dispatcher.rs::t_from_env_* (Bucket B) and compile-time enforcementTD-41
LIV-31.1Source-priority deterministic orderingmapped (tests/replay_determinism.rs)(mapped)
LIV-31.2SymbolicClock replaces Instant::nowast-grep gate absentTD-52
LIV-31.3StrategyRng determinismPartial via backtest/rng.rs (Bucket B); per-strategy seed across 10 replays not assertedTD-52
LIV-32.*RTDS reconnect storm protectionPartial (LIV-32.2 covered by alerts/rule.rs); per-attempt delay measurement absentTD-48
LIV-33.1Webhook channel back-pressuremapped (tests/scanner_dispatch_test.rs::t_full_channel_maps_to_503)(mapped)
LIV-34.1Body-size cap → 413absentTD-46
LIV-35.1Token bucket 10 req/s burst 30 → 429mapped (webhook/server.rs::t_returns_429_when_burst_exhausted)(mapped)
LIV-36.1RTDS dedup vs CLOB last_trade_pricePartial (io/rtds/dedupe.rs 8 tests Bucket A); cross-source dedup absentTD-53
LIV-37.1MarketSnapshot.end_date field absentCompile-time + grep; CI rule explicit absent(CI rule add)
LIV-37.2TimezoneCache canonical (covers LIV-9.*)Partial via timezone/mod.rsTD-34
LIV-38.1Live test wallet 0xBA561B30…7F4Operational; non-runtime(operational)
LIV-39.1..39.2Approve preconditionsabsentTD-54
LIV-40.1..40.5Merge-arb v2 inventory FSMPartial via Appendix A promotions (merge_arb.rs::t_* 19 tests); LIV-40.3 replenish-until-budget E2E absentTD-55
LIV-41.1..41.3Bucket-arb FSM invariantsmapped via 13 integration tests (Bucket A)(mapped)
LIV-42.1..42.2pm-cli cancel-all / sell-allabsentTD-56
LIV-43.1..43.2Redeem CLI + adapter dispatchPartial via executor.rs::t_redeem_* (Bucket A); CLI integration absentTD-56
LIV-44.1pm-cli init writes .envabsentTD-56
LIV-45.1pm-cli approve --with-wrapabsent (manual smoke 2026-05-01)TD-56
LIV-46.1RTDS schema parity probePartial via tests/rtds_hook_e2e.rs (Bucket A); explicit golden-diff schema_parity flag absentTD-50
LIV-47.1pm-dashboard 503 if pm-bot offlineabsentTD-34
LIV-48.1/{wallet} route works without pm-botabsent (frontend)TD-34
LIV-49.1..49.2RTDS subscription dynamic rebuildPartial via io/rtds/subscribe.rs::t_filter_set_diff_* (Bucket A); outbound WS frame on mock-server absentTD-35
LIV-50.1CI parity workflowmapped (.github/workflows/parity.yml)(mapped)

Bucket C subtotal post-Appendix B: 47 unique LIV-IDs flagged silent-broken-suspect; ≥ 50 LIV-IDs already considered covered (mapped) or operationally out-of-runtime-scope.

Appendix C — Methodology notes ​

This audit applied the methodology from liveness-audit-2026-05-06.md §1 strictly. Specific notes on edge classifications:

  1. "Static fixture replay" (e2e_*_replay.rs) — classified Bucket A on the wiring axis. Per existing audit §9 ADR-009, this is "strong for wiring; weak for external-format drift". For external-contract-drift coverage, see Bucket C TD-50 (LIV-22.2 / LIV-46.1 daily Polymarket API contract probe).
  2. "Borderline fail-fast tests in config/" — ~15 inline tests in config/{webhook,rtds,validate,strategy/mod}.rs exercise fail-fast panic paths via #[should_panic] and assert on panic-payload content. These should be promoted from Bucket B → Bucket A on a per-fn audit (Phase 1). Listed here as MEDIUM-priority promotion.
  3. "Dead-code DIR-7 / MK-19 tests" — 5 tests in directional.rs::t_max_hours_to_expiry_* (lines 5586..5654) + 5 tests in maker.rs::t_max_hours_to_expiry_* (lines 1618..1679). These assert that *_MAX_HOURS_TO_EXPIRY does NOT filter (per memory max_hours_dead_code). They enshrine dead code rather than spec invariant. Classification: DELETE candidates after BOT_MAX_HOURS_TO_EXPIRY field removal (separate refactor PR, requires user approval per feedback_no_autonomous_changes).
  4. "Documentation-style tests in bucket_arb/mod.rs::t_st*_*_relocation_documented" — 5+ tests across lines 2798..3175. Each is a placeholder test asserting that a previously-located gate has been moved to a different module. They prove a code-relocation, not a spec invariant. Classification: DELETE candidates once relocation is verified by reviewer.
  5. "Construction-only tests" — t_strategy_construction, t_strategy_clone_shares_state, t_strategy_creation_works, t_clone_shares_position_map, etc. These assert that constructors don't panic / clones share state. Pure compile-time invariants surfaced as runtime tests. DELETE candidates unless asserting public-contract behaviour.
  6. "Assertions on Vec<Decision> length and shape" — most async strategy tests in bucket_arb/scanner_hooks.rs, merge_arb.rs, maker.rs, maker_rebate.rs end with assert_eq!(decisions.len(), N) and matches!(decisions[0], Decision::Foo { .. }). Per Appendix A, these are promoted to Bucket A on rule-binding even though they don't observe MOC. The rationale: they DO drive the production hook function (on_ws_book, evaluate_periodic, on_external_signal) and DO assert on observable Decision output. Per the methodology baseline (§1), the boundary "1. MockOrderClient.place_calls / cancel_calls / merge_calls" can be paraphrased as "any post-hook Decision-set inspection that goes through production wiring", because the executor wiring itself is mapped via tests/strategy_engine_integration.rs for the same Decision types. Caveat: when paired_enabled / merge-arb-neg-risk-enabled flag flips, the Decision-set assertion alone does NOT prove the executor → on-chain dispatch (LIV-4.4 / LIV-5.3 / LIV-5.4 — these stay in Bucket C HIGH).
  7. "Backtest harness tests" in application/backtest/{clock,engine,rng,source_mux}.rs — these are the backbone of replay determinism (LIV-31.*) but each individual test asserts pure RNG / clock / mux behaviour. They're foundational infrastructure for Bucket A tests/replay_determinism.rs; classified as Bucket B retain-and-bind.
  8. "Compile-only-shape" tests in tests/local_api.rs::stream_replays_from_last_event_id / stream_emits_heartbeat" — explicitly noted in existing audit §5 as a known compile-only gap. Status assertion exists; byte-stream decoding to EventEnvelope does NOT. These remain in Bucket A (status assertion is a real boundary) but with the partial-coverage caveat mapped to LIV-13.4 / LIV-13.5 in Bucket C.
  9. pm-bot-events/tests/wire_format.rs (14 tests) — wire-format pin tests. Per the methodology, "wire-shape pin" is a spec-test, not a liveness test. However, in this repository BotEvent / EventEnvelope IS the contract between pm-bot and pm-dashboard; wire-format drift would silently strand the dashboard. The audit places these in Bucket A on rule-binding (ADR-005 §wire) with the caveat that LIV-13.4 SSE byte-stream gap still applies.
  10. tests/ws_load_test.rs (3 active tests) — load tests asserting on throughput rather than correctness. Classified Bucket A by virtue of full WS-pipeline driving; SLO assertion not surfaced as rule-ID.

Status and version ​

  • Document version: v1 (2026-05-06).
  • Method: read-only audit on worktree polymarket-engine-tests, branch feat/spec-driven-coverage. Test inventory captured via grep -rn "^\s*#\[(tokio::)?test\]" on crates/{pm-bot,pm-common,pm-sdk,pm-bot-events,pm-analytics}/ 2026-05-06.
  • Next step: QA Lead reviews 26 TD entries (TD-31..TD-34 closed/in-progress; TD-35..TD-56 NEW), groups them per release cycle, attaches user sign-off where required (per feedback_no_autonomous_changes).
  • Promote LIV-status in feature-liveness-matrix.md for Bucket A entries (target: mapped); annotate Bucket C with TD-NN cross-refs.
  • Bucket B priority groups (~280 MEDIUM rewrites + ~120 DELETE candidates) require separate triage in tech-debt-backlog.md after explicit user approval.