Тема
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:
- Spec-conformance — derived from a business rule (rule-ID), or code-derived tautology?
- 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):
| Class | Definition | Sufficient? |
|---|---|---|
| Spec test | Asserts 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 test | Drives 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 replay | E2E 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):
MockOrderClient.place_calls / cancel_calls / merge_calls / abandon_calls / persist_callsrecords.- SQLite journal row count via
read_conn.query_row(...). - SSE/REST response body via
axum_test_helper/reqwestagainstlocal_api::serve. - Tracing log line via
tracing_subscriber::fmt::TestWriter. Metrics::snapshot()/MetricsTickprojection 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):
| Crate | Inline #[cfg(test)] modules | Inline test fns | Integration files | Integration test fns | Total |
|---|---|---|---|---|---|
pm-bot | 93 | 1197 | 39 | 164 | 1361 |
pm-common | 6 | 74 | 0 | 0 | 74 |
pm-sdk | 15 | 92 | 0 | 0 | 92 |
pm-bot-events | 0 | 0 | 1 | 14 | 14 |
pm-analytics | 4 | 19 | 3 | 12 | 31 |
pm-cli | (counted under pm-bot via shared crates) | — | — | — | — |
| Workspace total | 118 | 1382 | 43 | 190 | ~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) covered | LIV-ID(s) covered | Boundary observed | Notes |
|---|---|---|---|---|
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.1 | MOC.place_calls | Asserts 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.1 | MOC.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-25 | LIV-3.1, LIV-11.2 | MOC.cancel_calls | Drives 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-17 | LIV-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.1 | MOC (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.1 | MOC.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-A | LIV-13.3 | J + broadcast | Asserts 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-31 | LIV-13.3 | J | Idempotent 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.3 | J + counter | Drops envelope when capacity exceeded; counter dropped_total increments. |
crates/pm-bot/tests/journal_writer.rs::commit_failure_propagates (line 231) | G-26 | LIV-13.3 | J + LOG | Commit 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-A | LIV-13.3 | broadcast seq | Strict 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 boundary | LIV-13.6 | J (bot_sessions) | Old session closes / new opens transactionally. |
crates/pm-bot/tests/journal_writer.rs::shutdown_drains_queue (line 442) | ADR-005 INV-A | LIV-13.3 | J | Shutdown path drains pending mpsc before exit. |
crates/pm-bot/tests/journal_writer.rs::snapshot_write_no_event_log_entry (line 485) | ADR-005 §snapshot | LIV-13.3 | J | Snapshot 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-26 | LIV-13.3 | J (basket_states) | UPDATE+INSERT in single tx. |
crates/pm-bot/tests/local_api.rs::health_returns_ok_with_session_id (line 165) | ADR-005 §health | LIV-13.1 | HTTP + body | GET /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 §seed | LIV-13.1 | HTTP + body | GET /seed JSON contains last_seq + session meta. |
crates/pm-bot/tests/local_api.rs::stream_replays_from_last_event_id (line 231) | ADR-005 §replay | LIV-13.5 | HTTP status only | Compile-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 §heartbeat | LIV-13.4 | HTTP status only | Same 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 §rotate | LIV-13.6 | HTTP status | Without 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 cousin | LIV-13.2 | TCP bind error | Bind 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.2 | HTTP + receiver mpsc | Real 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-9 | LIV-2.6 (partial — to-hook not to-Decision) | mpsc receiver | Confirms 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 modes | LIV-33.1 | HTTP 503 + counter | mpsc 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 compat | LIV-17.3 | TCP listener absent | WEBHOOK_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.4 | recorder + mock WS | Closes 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.1 | LIV-1.3, LIV-1.4 | recorder | Seconds-magnitude payload — exact silent-broken regression guard. |
crates/pm-bot/tests/rtds_hook_e2e.rs::t_invalid_timestamp_drops_event (line 331) | G-12 | LIV-1.3 | M (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.7 | MOC.cancel_calls + reason | Closes 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-13 | LIV-1.6 | LOG + 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-13 | LIV-1.6 | MOC.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-13 | LIV-27.2 | MOC.cancel_calls scoped | Cross-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 tick | M | Tick interval shape. |
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_respects_shutdown (line 183) | ADR-005 | M | Shutdown drains. | |
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_carries_emergency_flag (line 216) | ADR-005 + AlertRule cousin | M | Emergency flag propagates. | |
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_carries_session_id (line 254) | ADR-005 §session | M | session_id projected. | |
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_omits_rtds_when_unwired (line 299) | G-32 cousin | LIV-1.8 | M | RTDS counters absent when unwired. |
crates/pm-bot/tests/metrics_loop.rs::metrics_loop_projects_rtds_counters_when_wired (line 366) | G-12, G-13 indirect | LIV-1.8 | M | RTDS 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-mode | LIV-13.2 | EventHub state | NullHub.is_enabled() == false. |
crates/pm-bot/tests/event_hub.rs::event_hub_emit_round_trip (line 109) | ADR-005 INV-A | LIV-13.4 (partial) | broadcast Receiver | Envelope round-trips; full HTTP-SSE path not exercised. |
crates/pm-bot/tests/event_hub.rs::dropped_counter_increments_when_full (line 129) | ADR-005 backpressure | M | Drop counter. | |
crates/pm-bot/tests/event_hub.rs::null_hub_subscribe_does_not_panic (line 163) | ADR-005 §null-mode | LIV-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-mode | LIV-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-A | LIV-13.4 | broadcast | Cancel 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.5 | ON | Mock chain returns Confirmed → emit MergeConfirmed BotEvent. |
crates/pm-bot/tests/receipt_watcher.rs::receipt_watcher_drops_on_revert (line 176) | MA-11 (Reconcile path) | ON | Revert → MergeReverted event. | |
crates/pm-bot/tests/receipt_watcher.rs::receipt_watcher_drops_after_timeout (line 219) | MA-10 (Reconcile timeout) | ON | Timeout → 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.2 | MOC.place_calls subset | INV-20/21 sync cancel branch. | |
crates/pm-bot/tests/parallel_placement.rs::across_groups_sequential_in_groups_parallel (line 167) | BA-6 | timing | Parallelism shape across groups. | |
crates/pm-bot/tests/parallel_placement.rs::neg_risk_preresolved_passes_through_plan (line 208) | G-17, BA-1 | LIV-5.1 | MOC.place_calls + neg_risk flag | neg_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 taxonomy | LIV-14.1 | J + budget read | Apply 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-6 | MOC.cancel_calls len == legs | Cohort-cancel branch. | |
crates/pm-bot/tests/abandon_cancels_open_orders.rs::t_partial_reject_sync_cancels_posted_legs (line 440) | BA-6, INV-20/21 | LIV-41.2 | MOC.cancel_calls | Partial-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 isolation | LIV-41.2 | MOC.cancel_calls scoped | Cross-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-18 | LIV-16.5 cousin | budget 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-26 | LIV-41.* | J | Covered 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.* | J | Repair semantics. |
crates/pm-bot/tests/dirty_basket_retry.rs::t_transient_failure_requeues_basket_and_bumps_count (line 113) | BA-6 (resilience) + DIR-25 cousin | retry counter | Transient retry behaviour. | |
crates/pm-bot/tests/dirty_basket_retry.rs::t_max_retries_exhausted_moves_to_terminal (line 188) | BA-6 backoff | terminal state | Backoff caps. | |
crates/pm-bot/tests/dirty_basket_retry.rs::t_repost_classifier_partial_failure_requeues (line 283) | BA-6 | requeue | Reclassification on partial failure. | |
crates/pm-bot/tests/dirty_basket_retry.rs::t_repost_failure_escalates_after_max_retries (line 314) | BA-6 backoff | escalation | Escalation to terminal. | |
crates/pm-bot/tests/restart_attempt_recovery.rs::t_load_existing_orders_restores_attempt_from_basket_legs (line 48) | G-25, BA-18 | LIV-6.3 | J | Recovery 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-25 | LIV-6.3 | J | Cross-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-26 | LIV-6.3, LIV-6.4 | J | No-double-count after restart. |
crates/pm-bot/tests/crash_recovery.rs::st15_three_posted_legs_surface_on_startup (line 52) | G-25 | LIV-6.3 | J | Crash recovery surfaces all posted legs. |
crates/pm-bot/tests/crash_recovery.rs::st16_cancelled_legs_excluded (line 67) | G-25 | LIV-6.3 | J | Cancelled legs excluded from recovery list. |
crates/pm-bot/tests/crash_recovery.rs::st17_broken_basket_legs_surface_for_recovery (line 90) | G-25 | LIV-6.3 | J | Broken-basket legs surface. |
crates/pm-bot/tests/crash_recovery.rs::st17b_broken_basket_with_rejected_legs_visible_for_drain (line 114) | G-25 | LIV-6.3 | J | Drain semantics. |
crates/pm-bot/tests/crash_recovery.rs::st18_processed_trades_hydration (line 134) | G-25, G-26 | LIV-6.3 | J | Processed trades hydrate from journal. |
crates/pm-bot/tests/crash_recovery.rs::st19_order_hash_persists_through_lifecycle (line 160) | G-26 | J | Order hash invariant. | |
crates/pm-bot/tests/crash_recovery.rs::st20_empty_recovery_is_clean (line 195) | G-25 | J | Empty-DB recovery yields empty state. | |
crates/pm-bot/tests/crash_recovery.rs::st21_multi_basket_recovery_ordering (line 206) | G-30 | J | Multi-basket ordering preserved. | |
crates/pm-bot/tests/crash_recovery.rs::st22_covered_basket_still_surfaces_for_review (line 225) | G-25 | J | Covered baskets visible for review. | |
crates/pm-bot/tests/crash_recovery.rs::t15_partial_fill_hydration_uses_remaining_not_original (line 258) | G-26 | J | Hydration uses remaining size. | |
crates/pm-bot/tests/crash_recovery.rs::t15_unfilled_order_hydrates_at_full_original_size (line 286) | G-25 | J | Unfilled hydrates full size. | |
crates/pm-bot/tests/crash_recovery.rs::t15_fully_matched_live_order_clamps_to_zero (line 303) | G-25 | J | Fully-matched clamps. | |
crates/pm-bot/tests/manual_cancel_attempt.rs::t_cancel_order_uses_order_basket_leg_attempt_after_partial (line 77) | BA-22 | J + MOC.cancel_calls | Attempt-counter post-partial. | |
crates/pm-bot/tests/manual_cancel_attempt.rs::t_cancel_order_writes_attempt_1_when_no_prior_partial (line 173) | BA-22 | J | Attempt=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-22 | J | Expiry 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-22 | J | One-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-26 | J | Status 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 PnL | J + computed value | PnL correctness over partial stream. | |
crates/pm-bot/tests/ws_fill_before_insert_race.rs::t_control_insert_before_fill_creates_position (line 205) | G-26 | J | Baseline (control) for race. | |
crates/pm-bot/tests/ws_fill_before_insert_race.rs::t_race_fill_before_insert_eventually_creates_position (line 254) | G-26 | J + retry | Race-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-25 | J | REST 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-26 | LIV-12.3 | J + handler invocation | REST-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-20 | J | Reservation 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-26 | J + future-event tape | Future 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.3 | J | Terminal 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-6 | LIV-12.2 | FSM state | New market registers as Candidate. |
crates/pm-bot/tests/event_fsm_transitions.rs::t_register_multiple_events (line 122) | BA-2 | LIV-12.2 | FSM count | Multi-event registration. |
crates/pm-bot/tests/event_fsm_transitions.rs::t_candidate_to_tracking (line 139) | BA-13 | LIV-12.2 | FSM transition | Candidate→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-12 | abandon | Market-closed abandon. | |
crates/pm-bot/tests/event_fsm_transitions.rs::t_tracking_abandon_when_market_becomes_inactive (line 504) | G-22 | abandon | Inactive abandon. | |
crates/pm-bot/tests/event_fsm_transitions.rs::t_transition_to_participating_flips_phase (line 555) | BA-6 | FSM phase | Phase flip. | |
crates/pm-bot/tests/replay_determinism.rs::t_replay_ten_runs_yield_identical_hash (line 30) | G-14, G-30 | LIV-8.3, LIV-31.1 | SHA-256 stability | Canonical replay-parity gate. |
crates/pm-bot/tests/replay_determinism.rs::t_all_frozen_v1_fixtures_yield_consistent_hash (line 78) | G-14 | LIV-8.3 | SHA-256 stability | All fixtures stable. |
crates/pm-bot/tests/replay_determinism_smoke.rs::t_replay_determinism_10x_same_session_strategy_yields_same_digest (line 127) | G-14 | LIV-50.1 | SHA-256 | Smoke version. |
crates/pm-bot/tests/replay_determinism_smoke.rs::t_replay_independence_different_strategy_same_session_does_not_alias (line 167) | G-14 | LIV-50.1 | SHA-256 | Independence assertion. |
crates/pm-bot/tests/replay_determinism_smoke.rs::t_replay_different_capture_yields_different_digest (line 200) | G-14 negative | LIV-50.1 | SHA-256 differs | Negative replay parity. |
crates/pm-bot/tests/capture_smoke.rs::t_jsonl_zstd_roundtrip_preserves_frames (line 76) | G-15 | LIV-8.1 | FS | Capture file round-trips. |
crates/pm-bot/tests/capture_smoke.rs::t_parquet_frozen_v1_schema_matches_jsonl (line 122) | G-15 | LIV-8.1 | FS schema | Schema parity. |
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_1_jsonl_roundtrip_preserves_frames (line 150) | G-15 | LIV-8.1 | FS | JSONL gate. |
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_2_golden_hash_stable_across_ten_runs (line 197) | G-14 | LIV-8.3 | SHA-256 | Golden gate. |
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_2b_all_fixtures_yield_consistent_hash (line 217) | G-14 | LIV-8.3 | SHA-256 | All-fixtures gate. |
crates/pm-bot/tests/capture_replay_parity.rs::t_gate_3_parquet_schema_matches_current (line 247) | G-15 | LIV-8.1 | FS schema | Parquet 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 indirect | LIV-8.3 + bucket-arb suite | full pipeline | Static 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-32 | LIV-8.3 | full pipeline | Yes profile equivalence. |
crates/pm-bot/tests/e2e_directional_replay.rs::t_directional_no_profile_cyyz (line 172) | DIR-3, DIR-19 | LIV-8.3 | full pipeline | No profile. |
crates/pm-bot/tests/e2e_directional_replay.rs::t_directional_harvester_profile_cyyz (line 192) | DIR-12, DIR-13, DIR-19 | LIV-3.* | full pipeline | Harvester profile. |
crates/pm-bot/tests/e2e_directional_replay.rs::t_directional_quickflip_profile_cyyz (line 217) | DIR-18, DIR-19 | LIV-8.3 | full pipeline | QuickFlip 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 indirect | LIV-4.* | full pipeline | Maker-rebate replay. |
crates/pm-bot/tests/e2e_maker_replay.rs::t_maker_no_maker_profile_cyyz (line 163) | MK-15, MK-16 | LIV-8.3 | full pipeline | NoMaker profile. |
crates/pm-bot/tests/e2e_maker_replay.rs::t_maker_spread_maker_profile_cyyz (line 183) | MK-14, MK-16 | LIV-8.3 | full pipeline | SpreadMaker profile. |
crates/pm-bot/tests/e2e_maker_replay.rs::t_maker_sniper_profile_cyyz (line 206) | MK-13, MK-16 | LIV-8.3 | full pipeline | Sniper profile. |
crates/pm-bot/tests/session_lifecycle.rs::start_creates_session_row (line 56) | G-31, ADR-005 §session | LIV-13.1 | J (bot_sessions) | Session row inserted. |
crates/pm-bot/tests/session_lifecycle.rs::rotate_closes_old_opens_new (line 82) | ADR-005 §rotate | LIV-13.6 | J | Rotate atomicity. |
crates/pm-bot/tests/session_lifecycle.rs::crash_recovery_marks_stale_session_ended (line 103) | G-25, ADR-005 | J | Stale session end. | |
crates/pm-bot/tests/session_lifecycle.rs::concurrent_instance_fails_fast (line 137) | ADR-005 INV (single-active) | PA | Second instance fails fast. | |
crates/pm-bot/tests/session_lifecycle.rs::heartbeat_updates_last_heartbeat_at (line 165) | ADR-005 §heartbeat | J | Heartbeat 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) | J | Schema install. | |
crates/pm-bot/tests/journal_migrations.rs::idempotent_double_run (line 163) | G-31 | J | Idempotent migration. | |
crates/pm-bot/tests/journal_migrations.rs::rejects_legacy_ladder_db (line 174) | G-31 | PA | Legacy schema rejected. | |
crates/pm-bot/tests/journal_migrations.rs::bot_sessions_mode_defaults_to_live (line 197) | ADR-005 + ADR-009 §bot_sessions.mode | J | Default mode='live'. | |
crates/pm-bot/tests/journal_migrations.rs::uq_active_session_enforces_one_active (line 221) | ADR-005 single-active | J constraint | Unique-constraint enforces one active. | |
crates/pm-bot/tests/journal_migrations.rs::uq_event_causal_key_idempotency (line 259) | G-26 | J constraint | Idempotency 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-31 | J | Convergence of two migrators. | |
crates/pm-bot/tests/flush_ack_ordering.rs::shutdown_acks_post_commit (line 32) | ADR-005 INV-A | J + ack | Ack 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) | M | Metric 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-18 | LIV-11.1 | MOC.place_calls | Inline-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-17 | LIV-11.2 | MOC.cancel_calls | Cancel 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 + status | Batch resilience. | |
crates/pm-bot/src/application/executor.rs::t_execute_gtd_default_horizon_applied (line 2135) | DIR-25 cousin | MOC.place_calls + GTD | GTD horizon. | |
crates/pm-bot/src/application/executor.rs::t_execute_empty_batch_returns_empty (line 2148) | G-4 | MOC (empty) | Empty batch. | |
crates/pm-bot/src/application/executor.rs::t_execute_hold_is_ok_noop (line 2155) | G-27, DIR-27 | MOC (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-21 | LIV-43.1 | redeem-mock | Standard redeem. |
crates/pm-bot/src/application/executor.rs::t_redeem_neg_risk_ok_with_both_tokens (line 2191) | G-18, G-19 | LIV-43.2 | NegRiskAdapter mock | neg_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) | status | Client failure surfaced. | |
crates/pm-bot/src/application/executor.rs::t_abandon_ok_invokes_client (line 2282) | BA-18 | MOC.abandon_calls | Abandon 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-27 | MOC.cancel + place | Modify = cancel+place. | |
crates/pm-bot/src/application/executor.rs::t_modify_cancel_fails_does_not_place (line 2354) | BA-9 negative | MOC.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 + diagnostic | Diagnostic on partial failure. | |
crates/pm-bot/src/application/executor.rs::t_persist_with_client_writes_through (line 2432) | G-26 | MOC.persist | Write-through observable. | |
crates/pm-bot/src/application/executor.rs::t_persist_with_client_failure_surfaces_failed (line 2463) | G-26 | status | Failure surfaced. | |
crates/pm-bot/src/application/executor.rs::t_execute_post_basket_complete_returns_ok (line 2567) | BA-6 | MOC | PostBasket ok. | |
crates/pm-bot/src/application/executor.rs::t_execute_post_basket_partial_returns_partial (line 2602) | BA-6 | MOC + partial status | PostBasket partial. | |
crates/pm-bot/src/application/executor.rs::t_execute_post_basket_all_failed_returns_failed (line 2637) | BA-6 | MOC + all-failed | PostBasket failure. | |
crates/pm-bot/src/application/executor.rs::t_dryrun_no_live_clob_calls (line 2673) | G-3 | MOC (empty live) | Dry-run isolation. | |
crates/pm-bot/src/application/engine.rs:557..659 (5 anonymous tests) | G-3, G-4 | LIV-11.* | MOC | Five 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.2 | LIV-1.9 | AlertRule emit | Closes 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.1 | LIV-1.9 | AlertRule | Fires on stale-drop. |
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_fires_on_reconnect_storm (line 794) | ADR-004 §reconnect | LIV-1.9, LIV-32.2 | AlertRule | Fires on reconnect storm. |
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_fires_on_stuck_subscription (line 820) | ADR-004 freshness | LIV-1.9 | AlertRule | Fires on stuck subscription. |
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_hysteresis (line 854) | ADR-004 | LIV-1.9 | AlertRule | Hysteresis prevents flap. |
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_no_fire_when_healthy (line 873) | ADR-004 | LIV-1.9 | AlertRule (silent) | Healthy → no fire. |
crates/pm-bot/src/application/alerts/rule.rs::t_rtds_health_no_fire_when_metrics_absent (line 898) | ADR-004 | LIV-1.9 | AlertRule (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-004 | LIV-1.9 | AlertRule | Threshold=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-17 | LIV-12.4 | LIV-43.2 | Resolution 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 filter | LIV-1.2 | (filter set) | Collects event-slugs. |
crates/pm-bot/src/application/scanner.rs::t_collect_filters_directional_returns_market_slugs (line 2519) | RTDS subscribe | LIV-1.2 | (filter set) | Collects market-slugs. |
crates/pm-bot/src/io/webhook/server.rs::t_returns_202_on_valid_signal (line 498) | G-9 | LIV-2.2 | HTTP | Valid HMAC → 202. |
crates/pm-bot/src/io/webhook/server.rs::t_returns_401_bad_sig (line 512) | G-9 negative | LIV-2.2 | HTTP | Bad 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-limit | LIV-35.1 | HTTP 429 | Burst exhausted. |
crates/pm-bot/src/io/webhook/server.rs::t_returns_409_on_duplicate (line 580) | G-10 | LIV-7.1 | HTTP 409 | Duplicate 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-11 | LIV-2.4 | HTTP 401 | IP 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-pressure | LIV-33.1 | HTTP 503 | Channel-full → 503. |
crates/pm-bot/src/io/webhook/server.rs::t_returns_400_on_idempotency_mismatch (line 684) | G-10 negative | LIV-7.1 | HTTP 400 | Idempotency mismatch. |
crates/pm-bot/src/io/webhook/server.rs::t_rate_limiter_allows_burst_then_blocks (line 710) | ADR-003 | LIV-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-003 | LIV-35.1 | (limiter state) | Refill semantics. |
crates/pm-bot/src/io/webhook/server.rs::t_logs_never_contain_secret (line 745) | G-9 | LOG (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 §bind | LIV-13.1 | TCP | Loopback v4 bind. |
crates/pm-bot/src/io/local_api/mod.rs::t_bind_accepts_loopback_v6 (line 376) | ADR-005 §bind | LIV-13.1 | TCP | Loopback v6 bind. |
crates/pm-bot/src/io/local_api/mod.rs::t_bind_rejects_non_loopback_without_flag (line 395) | ADR-005 §bind-policy | LIV-13.1 | TCP refuse | Non-loopback rejected. |
crates/pm-bot/src/io/local_api/mod.rs::t_bind_allows_non_loopback_with_flag (line 405) | ADR-005 §bind-policy | LIV-13.1 | TCP | Non-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.1 | LIV-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-20 | LIV-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-5 | LIV-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-5 | LIV-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 cousin | LIV-16.5 | (predicate scope) | Per-wallet isolation. |
crates/pm-bot/src/io/rtds/parse.rs::t_parse_trade_event_happy_path (line 473) | G-12 | LIV-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.1 | LIV-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.1 | LIV-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 Q2 | LIV-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 §D7 | LIV-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.1 | LIV-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-10 | LIV-7.1 | (dedup state) | Dedup core. |
crates/pm-bot/src/io/webhook/dedupe.rs::t_dedupe_evicts_after_ttl (line 172) | G-10 | LIV-7.1 | (TTL eviction) | TTL bounded. |
crates/pm-bot/src/io/webhook/dedupe.rs::t_dedupe_capacity_enforced (line 187) | G-10 | LIV-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-9 | LIV-2.2 | (HMAC accept/reject) | HMAC accept/reject. |
crates/pm-bot/src/timezone/mod.rs (16 tests) | G-1, ADR-016 | LIV-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 model | LIV-18.2 | J | capture_sessions schema install. |
crates/pm-bot/src/persistence/journal/mod.rs::t_capture_sessions_status_check_constraint (line 492) | ADR-009 | J constraint | Status constraint. | |
crates/pm-bot/src/persistence/journal/reader.rs::t_journal_reader_recent_terminal_watch_arm_fak_cids_returns_done_states (line 333) | ADR-017 | LIV-6.3 | J | Reader 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-017 | LIV-6.3 | J | Latest-per-cid. |
crates/pm-analytics/tests/replay_smoke.rs::* (5 tests) | G-14 | LIV-8.3 | full pipeline | pm-analytics replay smoke. |
crates/pm-analytics/tests/whatif_integration.rs::* (3 tests) | ADR-009 §Phase 6 | LIV-19.3 | CLI | What-if env-override via CLI. |
crates/pm-analytics/tests/sweep_integration.rs::* (4 tests) | ADR-009 §Phase 5 | LIV-19.1 | CLI + filesystem | Sweep parallelism. |
crates/pm-bot-events/tests/wire_format.rs::* (14 tests) | ADR-005 §wire | LIV-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 tautology | Suggested rule-ID to bind | Rewrite 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-21 | LOW — 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-10 | LOW — 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-30 | MEDIUM — 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-21 | MEDIUM — 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 §D5 | MEDIUM — 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-16 | LOW — 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-6 | LOW |
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 indirect | DELETE 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-14 | MEDIUM — 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-30 | MEDIUM |
crates/pm-bot/src/domain/strategies/directional.rs::t_dca_replaces_buy_on_partial_fill (line 6351) | Pure DCA decision. | DIR-15, DIR-16 | MEDIUM — 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-16 | LOW |
crates/pm-bot/src/domain/strategies/directional.rs::t_recompute_exit_on_full_fill_is_off_by_default (line 6438) | Pure flag. | DIR-30 | LOW |
crates/pm-bot/src/domain/strategies/directional.rs — remaining ~88 inline tests across lines 6438..end | Mostly pure evaluator gates / config tests / FSM transition assertions on synthetic state. | DIR-* group | Aggregate 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-26 | MEDIUM — promote to journal-observed |
crates/pm-bot/src/application/order_manager.rs::t_autosell_* (lines 5558..5727, 6 tests) | Pure tick-rounding math. | BA-10 | LOW — 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-8 | MEDIUM — 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-27 | MEDIUM |
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-20 | MEDIUM — 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-22 | LOW — 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-26 | Aggregate 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-22 | MEDIUM — 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-28 | LOW |
crates/pm-bot/src/domain/strategies/bucket_arb/fsm.rs::t_r8_2_drift_* (lines 835..890, 5 tests) | Pure drift threshold math. | BA-9 | LOW |
crates/pm-bot/src/domain/strategies/bucket_arb/fsm.rs::t_task13_timeout_* (lines 921..984, 5 tests) | Pure timeout math. | BA-12 | LOW |
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-4 | DELETE 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-26 | MEDIUM — 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 cousin | MEDIUM |
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-17 | MEDIUM — 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-3 | LOW |
crates/pm-bot/src/domain/strategies/maker_rebate.rs — remaining ~31 inline tests | Pure 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-9 | MEDIUM — 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-11 | MEDIUM — rewrite as MOC.merge_calls (LIV-5.3 / 5.4 dispatch) |
crates/pm-bot/src/domain/strategies/merge_arb.rs — remaining ~18 tests | Pure 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-12 | LOW — 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-16 | MEDIUM — 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-20 | LOW — 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-30 | LOW |
crates/pm-bot/src/domain/strategies/shared/time.rs (8 inline tests) | Pure deadline math. | G-1 | LOW — 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-21 | LOW |
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-strategy | LOW |
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-12 | LOW — 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.8 | MEDIUM — 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.1 | LOW |
crates/pm-bot/src/io/rtds/smoke.rs (3 inline tests) | Smoke-report shape. | RCA §6.2 LIV-22.1 | MEDIUM — 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.1 | MEDIUM — 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.5 | LOW |
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 tests | Pure simulator state asserts. | G-3 | LOW |
crates/pm-bot/src/io/clob/simulator/mock_clob.rs — 6 tests beyond Bucket A | Pure 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 indirect | LOW — 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 A | DB CRUD. | ADR-015 §D1 | LOW — keep as regression |
crates/pm-bot/src/persistence/strategy_config.rs — remaining ~30 tests beyond Bucket A | DB CRUD + extract_definition_from_env. | ADR-015 §D6 | LOW |
crates/pm-bot/src/persistence/journal/gc.rs (10 inline tests) | Pure GC math. | ADR-009 §retention | LOW |
crates/pm-bot/src/persistence/journal/reader.rs (5 inline tests) | DB reader. | ADR-017 | LOW (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.1 | LOW |
crates/pm-bot/src/persistence/capture/writer.rs (2 inline tests) | Pure writer state. | LIV-8.1 | LOW |
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 §retention | LOW |
crates/pm-bot/src/application/dispatcher.rs::t_from_env_* (lines 360..382, 4 tests) | Pure env-→-StrategyImpl mapping. | G-5, ADR-008 | LOW — bind to G-5 |
crates/pm-bot/src/application/scanner.rs — 5 tests (3 in Bucket A) — remaining 2 | Pure resolution-sweep tests. | BA-17 | LOW |
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 tests | Other 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-30 | LOW — keep as backtest regression |
crates/pm-bot/src/application/engine.rs — remaining 2 tests beyond Bucket A | Pure 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-30 | LOW |
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-4 | LOW |
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-25 | LOW |
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-2 | LOW (G-2 already enforced via grep gate) |
crates/pm-common/src/retry.rs (3 tests) | Pure retry math. | DIR-25 | LOW |
crates/pm-sdk/src/clob/wire.rs (11 tests) | Pure JSON wire-shape tests. | BA-15, BA-16 | LOW — pair with golden fixture probe (RCA §6.4 P2) |
crates/pm-sdk/src/validation.rs (9 tests) | Pure ClobAuth validation rules. | BA-15, BA-16 | LOW |
crates/pm-sdk/src/eip712/order.rs (9 tests) | Pure EIP-712 hash tests. | BA-16 | LOW — math primitive; bind to BA-16 |
crates/pm-sdk/src/eip712/mod.rs (7 tests) | Pure typehash tests. | BA-16 | LOW |
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-16 | LOW |
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.1 | LOW — paired with manual smoke 2026-05-01 |
crates/pm-sdk/src/onchain/ctf.rs (3 tests) | Pure CTF call construction. | G-18, LIV-5.3 | MEDIUM — pair with merge-arb dispatch test gap |
crates/pm-sdk/src/onchain/balance.rs (3 tests) | Pure balance call. | LIV-29.5 | LOW |
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.1 | LOW |
crates/pm-analytics/src/whatif/filter.rs (6 tests) | Pure filter. | LIV-19.3 | LOW |
crates/pm-analytics/src/whatif/diff.rs (5 tests) | Pure diff. | LIV-19.3 | LOW |
crates/pm-analytics/src/whatif/journal_reader.rs (4 tests) | Pure journal reader. | LIV-19.3 | LOW |
crates/pm-analytics/src/sweep/report.rs (1 test) | Pure report. | LIV-19.2 | LOW |
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 tautology | Suggested rule-ID | Rewrite 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-30 | MEDIUM — 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-19 | MEDIUM — 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.3 | MEDIUM — 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-5 | LOW |
directional.rs::t_record_pending_entry_dedupes_repeated_calls (line 6885) | Pure dedup state-mutation. | LIV-7.2 cousin | MEDIUM |
directional.rs::t_has_active_entry_covers_pending_live_and_positions (line 6902) | Pure predicate over union of states. | DIR-15 cousin | LOW |
directional.rs::t_after_execute_promotes_entry_pending_to_live (line 6945) | Pure state-mutation; FSM transition. | DIR-22 (recovery) cousin | MEDIUM |
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-22 | LOW |
directional.rs::t_ttl_only_evicts_awaiting_ack (line 7060) | Pure TTL eviction. | DIR-25 | LOW |
directional.rs::t_full_fill_removes_pending_but_position_blocks_reentry (line 7097) | Pure FSM state. | LIV-7.2 cousin | MEDIUM |
directional.rs::t_partial_fill_keeps_pending_lock (line 7136) | Pure FSM state. | DIR-30 | LOW |
directional.rs::t_on_cancel_clears_pending_lock (line 7181) | Pure cleanup on cancel. | DIR-22 | LOW |
directional.rs::t_invariant_stop_lt_min_entry_violated (line 7262) | Pure invariant validator (negative). | DIR-12 | LOW — keep as invariant suite |
directional.rs::t_invariant_stop_eq_min_entry_violated (line 7286) | Pure invariant validator. | DIR-12 | LOW |
directional.rs::t_invariant_stop_lt_max_entry_violated_no_min (line 7300) | Pure. | DIR-12 | LOW |
directional.rs::t_invariant_exit_gt_max_entry_violated (line 7318) | Pure. | DIR-11, DIR-12 | LOW |
directional.rs::t_invariant_min_lt_max_violated (line 7337) | Pure. | DIR-12 | LOW |
directional.rs::t_invariant_price_out_of_range (line 7356) | Pure. | DIR-12 | LOW |
directional.rs::t_invariants_all_pass_for_harvester_preset (line 7404) | Pure (positive case). | DIR-12 | LOW |
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-25 | MEDIUM — 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-25 | LOW |
directional.rs::t_pending_lifecycle_full (line 7594, async) | Pure lifecycle traversal. | DIR-22 | MEDIUM |
directional.rs::t_sweep_branch_forces_post_only_false_when_limit_post_only_set (line 7704) | Pure flag-routing in compute_sweep. | DIR-31 cousin | LOW |
directional.rs::t_validate_invariants_rejects_sweep_limit_post_only_combo (line 7781) | Pure validator. | DIR-12 | LOW |
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-017 | LOW |
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-6 | LOW |
directional.rs::t_watch_arm_fak_limit_price_capped_by_arm_limit_max_price (line 8035) | Pure min() composition. | ADR-017 | LOW |
directional.rs::t_watch_arm_fak_revalidate_drops_if_prob_decreased (line 8058) | Pure revalidation. | ADR-017, DIR-5 | LOW |
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-017 | LOW |
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-017 | LOW |
directional.rs::t_watch_arm_fak_trigger_mid_fires_when_midpoint_crosses (line 8262) | Pure trigger logic. | ADR-017 | LOW |
directional.rs::t_watch_arm_fak_trigger_last_trade_fires_when_present (line 8308) | Pure trigger logic. | ADR-017 | LOW |
directional.rs::t_watch_arm_fak_deadline_cutoff_uses_local_midnight (line 8369) | Pure deadline math via TZ cache. | G-1, ADR-016 | MEDIUM — pair with LIV-9.3 |
directional.rs::t_validate_invariants_rejects_arm_post_only_true_with_watch_arm_fak (line 8422) | Pure validator. | DIR-12 | LOW |
directional.rs::t_validate_invariants_rejects_arm_order_type_gtd_with_watch_arm_fak (line 8437) | Pure validator. | DIR-12 | LOW |
directional.rs::t_validate_invariants_rejects_sweep_with_watch_arm_fak (line 8450) | Pure validator. | DIR-12 | LOW |
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-017 | MEDIUM — 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-017 | LOW |
directional.rs::t_watch_arm_fak_exit_uses_filled_size_not_original_size (line 8579) | Pure size logic. | DIR-22 | LOW |
directional.rs::t_watch_arm_fak_revalidate_after_fill_passes_emits_exit (line 8673, async) | Pure revalidation + Decision shape. | ADR-017, DIR-30 | MEDIUM |
directional.rs::t_watch_arm_fak_revalidate_after_fill_fails_cancels_exit (line 8745, async) | Pure revalidation + cancel Decision. | ADR-017 | MEDIUM |
directional.rs::t_watch_arm_fak_emits_state_transition_event_on_admit (line 8911) | Pure event emit on FSM transition. | ADR-017 | MEDIUM — pair with journal observation |
directional.rs::t_watch_arm_fak_recovery_skips_terminal_cids (line 8963) | Pure recovery skip logic. | G-25, ADR-017 | MEDIUM — 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-23 | MEDIUM — 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.5 | MEDIUM |
directional.rs::t_directional_webhook_market_not_in_cache_rejected (line 9228, async) | Pure rejection. | LIV-2.5 cousin | LOW |
directional.rs::t_directional_webhook_dedup_when_position_open (line 9259, async) | Pure dedup. | LIV-7.2 | MEDIUM |
directional.rs::t_directional_external_signal_market_in_cache_no_fetch (line 9373, async) | Pure cache-hit path. | LIV-2.6, DIR-23 | MEDIUM |
directional.rs::t_directional_external_signal_market_not_in_cache_fetches_and_evaluates (line 9413, async) | Pure cache-miss + fetch. | LIV-2.6, DIR-23 | MEDIUM |
directional.rs::t_directional_external_signal_fetch_fails_returns_empty (line 9454, async) | Pure error path. | DIR-25 | LOW |
directional.rs::t_directional_rtds_below_threshold_no_action (line 9548, async) | Pure RTDS toxic-flow filter (negative). | G-12, DIR-24 | MEDIUM — 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.5 | LOW |
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-24 | MEDIUM — 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 Q2 | LOW |
directional.rs::t_last_trade_price_populated_from_ws_market_trade_event (line 9830) | Pure populate. | LIV-12.3 | LOW |
directional.rs::t_last_trade_price_populated_from_rtds_trade_event (line 9855) | Pure populate from RTDS. | LIV-1.4 | MEDIUM |
directional.rs::t_last_trade_price_returns_none_when_stale_beyond_ttl (line 9881) | Pure TTL. | G-28 | LOW |
directional.rs::t_watch_arm_fak_trigger_last_trade_now_fires_in_e2e (line 9928) | Pure E2E-shape composition (still synthetic). | ADR-017 | MEDIUM |
directional.rs::t_ev_cap_disabled_when_source_unset (line 10143) | Pure flag. | ADR-017 | LOW |
directional.rs::t_ev_cap_log_only_logs_would_reject_but_emits_anyway (line 10169) | Pure log + emit. Borderline — log boundary candidate. | ADR-017 | MEDIUM |
directional.rs::t_ev_cap_active_skips_emit_when_limit_exceeds_ev (line 10202) | Pure skip. | ADR-017 | LOW |
directional.rs::t_ev_cap_active_emits_when_limit_within_ev (line 10236) | Pure emit. | ADR-017 | LOW |
directional.rs::t_validate_invariants_rejects_unsupported_fair_value_source (line 10270) | Pure validator. | DIR-12 | LOW |
directional.rs::t_validate_invariants_accepts_both_ev_cap_mode_variants (line 10293) | Pure validator. | DIR-12 | LOW |
directional.rs::t_validate_invariants_rejects_negative_min_edge_abs (line 10314) | Pure validator. | DIR-12 | LOW |
directional.rs::t_validate_invariants_rejects_negative_min_roi (line 10331) | Pure validator. | DIR-12 | LOW |
directional.rs::t_ev_cap_skipped_when_producer_returns_none (line 10351) | Pure None-handling. | ADR-017 | LOW |
directional.rs::t_ev_cap_uses_adr017_defaults_when_knobs_unset (line 10392) | Pure defaults. | ADR-017 | LOW |
directional.rs::t_seed_active_states_pre_populates_watch_states_map (line 10486) | Pure state-seed. | G-25, ADR-017 | MEDIUM |
directional.rs::t_recovered_watching_state_preserves_since_timestamp_for_timeout_check (line 10543) | Pure since-timestamp preservation. | G-25, ADR-017 | LOW |
directional.rs::t_seed_active_states_skips_blacklisted_cids (line 10583) | Pure blacklist filter. | G-25 | LOW |
(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 tautology | Suggested rule-ID | Rewrite priority |
|---|---|---|---|
order_manager.rs::t_apply_partial_fill_shrinks_remaining (line 7679) | Pure remaining-size update. | G-26 | MEDIUM — 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-26 | LOW |
order_manager.rs::t_partial_fill_then_expire_updates_basket_legs_size (line 7766) | Pure update on expire. | G-26, BA-22 | LOW |
order_manager.rs::t_apply_partial_fill_overfill_clamps_to_full (line 7825) | Pure clamp. | G-26 | LOW |
order_manager.rs::t_unknown_status_ignored (line 7856) | Pure unknown-status no-op. | G-4 | LOW |
order_manager.rs::t_partial_fill_then_expire_repost_uses_remaining_size (line 7908) | Pure repost size. | BA-22 | MEDIUM |
order_manager.rs::t_repeated_partial_fills_accumulate_position (line 8018) | Pure accumulation. | G-26 | LOW |
order_manager.rs::t_partial_fills_weighted_avg_price (line 8102) | Pure weighted avg. | ADR-014 PnL | LOW |
order_manager.rs::t_mark_basket_dirty_is_idempotent (line 8171) | Pure idempotency. | BA-13 | LOW |
order_manager.rs::t_mark_basket_dirty_empty_slug_noop (line 8195) | Pure no-op edge. | BA-13 | LOW |
order_manager.rs::t_partial_placement_marks_basket_dirty (line 8212) | Pure mark. | BA-22 | LOW |
order_manager.rs::t_td2_repost_ceiling_patient_maker_uses_min_profit_pct (line 8272) | Pure repost ceiling. | BA-4 | LOW |
order_manager.rs::t_td2_repost_ceiling_immediate_fill_uses_target_profit (line 8285) | Pure repost ceiling. | BA-4 | LOW |
order_manager.rs::t_td2_repost_ceiling_immediate_fill_target_profit_dominates_min_profit (line 8300) | Pure dominance rule. | BA-4 | LOW |
order_manager.rs::t_td2_repost_ceiling_immediate_fill_zero_target_profit_collapses_to_one (line 8319) | Pure edge. | BA-4 | LOW |
(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 tautology | Suggested rule-ID | Rewrite priority |
|---|---|---|---|
bucket_arb/mod.rs::t_evaluate_basket_ready_3legs (line 1381) | Pure FSM evaluator returns Ready. | BA-3, BA-4 | MEDIUM — 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-21 | LOW |
bucket_arb/mod.rs::t_evaluate_basket_over_budget (line 1470) | Pure budget gate. | G-20, BA-11 | LOW |
bucket_arb/mod.rs::t_evaluate_basket_missing_book (line 1496) | Pure missing-book skip. | G-28 | LOW |
bucket_arb/mod.rs::t_evaluate_basket_missing_book_zero_bid (line 1512) | Pure edge. | G-28 | LOW |
bucket_arb/mod.rs::t_evaluate_basket_empty_legs_is_missing_book (line 1528) | Pure edge. | BA-14 | LOW |
bucket_arb/mod.rs::t_immediate_fill_ready_when_sum_plus_target_le_one (line 1541) | Pure threshold. | BA-4 | LOW |
bucket_arb/mod.rs::t_immediate_fill_skip_when_margin_insufficient (line 1577) | Pure margin gate. | BA-4 | LOW |
bucket_arb/mod.rs::t_immediate_fill_sum_above_one_still_skips (line 1606) | Pure rule. | BA-4 | LOW |
bucket_arb/mod.rs::t_immediate_fill_missing_ask_returns_missing_book (line 1626) | Pure edge. | BA-2 | LOW |
bucket_arb/mod.rs::t_immediate_fill_respects_budget (line 1642) | Pure budget gate. | G-20, BA-11 | LOW |
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-4 | LOW |
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-10 | LOW |
bucket_arb/mod.rs::t_on_partial_fill_with_autosell_emits_gtc_sell (line 1860) | Pure Decision shape. | BA-10 | MEDIUM — 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-4 | LOW |
bucket_arb/mod.rs::t_on_full_fill_marks_basket_filled_when_all_legs_done (line 1907) | Pure FSM state. | BA-6 | MEDIUM |
bucket_arb/mod.rs::t_on_full_fill_with_autosell_sells_remaining_delta (line 1927) | Pure Decision shape. | BA-10 | MEDIUM |
bucket_arb/mod.rs::t_on_cancel_marks_leg_and_abandons_when_no_activity (line 1955) | Pure cancel handling. | BA-22, BA-9 | LOW |
bucket_arb/mod.rs::t_on_cancel_keeps_basket_when_filled_legs_exist (line 1976) | Pure abandon-vs-keep. | BA-22 | LOW |
bucket_arb/mod.rs::t_on_market_resolved_emits_redeem_per_filled_leg (line 2003) | Pure redeem Decision. | BA-17 | MEDIUM — 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-17 | LOW |
bucket_arb/mod.rs::t_on_tick_size_change_updates_leg_tick_no_cancel_when_on_grid (line 2131) | Pure tick-size update. | BA-9 | LOW |
bucket_arb/mod.rs::t_on_tick_size_change_off_grid_emits_cancel (line 2150) | Pure off-grid cancel. | BA-9 | LOW |
bucket_arb/mod.rs::t_evaluate_periodic_fresh_basket_is_noop (line 2179) | Pure freshness gate. | BA-12 | LOW |
bucket_arb/mod.rs::t_evaluate_periodic_emits_cancels_after_timeout (line 2189) | Pure timeout cancel. | BA-12 | MEDIUM — 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-22 | LOW |
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_patient_uses_maker_safe (line 2278) | Pure mode routing. | BA-21 | LOW |
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_immediate_fill_uses_best_ask (line 2290) | Pure mode routing. | BA-21 | LOW |
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_returns_none_on_missing_book (line 2302) | Pure None edge. | G-28 | LOW |
bucket_arb/mod.rs::t_td3_compute_basket_live_sum_returns_none_on_stale_book (line 2316) | Pure stale guard. | G-28 | LOW |
bucket_arb/mod.rs::t_td3_should_cancel_patient_below_ceiling_keeps_basket (line 2332) | Pure threshold. | BA-9 | LOW |
bucket_arb/mod.rs::t_td3_should_cancel_patient_above_ceiling_plus_margin_cancels (line 2341) | Pure threshold. | BA-9 | LOW |
bucket_arb/mod.rs::t_td3_should_cancel_patient_at_margin_boundary_keeps_basket (line 2350) | Pure boundary. | BA-9 | LOW |
bucket_arb/mod.rs::t_td3_should_cancel_immediate_fill_below_ceiling_keeps_basket (line 2359) | Pure threshold. | BA-9 | LOW |
bucket_arb/mod.rs::t_td3_should_cancel_immediate_fill_above_threshold_cancels (line 2369) | Pure threshold. | BA-9 | LOW |
bucket_arb/mod.rs::t_td3_should_cancel_immediate_fill_at_admission_boundary_keeps_basket (line 2377) | Pure boundary. | BA-9 | LOW |
bucket_arb/mod.rs::t_td3_evaluate_periodic_emits_drift_cancels_patient_mode (line 2388) | Pure drift cancel. | BA-9 | MEDIUM |
bucket_arb/mod.rs::t_td3_evaluate_periodic_emits_drift_cancels_immediate_fill_mode (line 2413) | Pure drift cancel. | BA-9 | MEDIUM |
bucket_arb/mod.rs::t_td3_evaluate_periodic_no_drift_emits_no_cancels (line 2436) | Pure negative. | BA-9 | LOW |
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-22 | LOW |
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-10 | LOW |
bucket_arb/mod.rs::t_autosell_one_percent_tick_001 (line 2513) | Pure tick math. | BA-10 | LOW |
bucket_arb/mod.rs::t_autosell_sub_tick_pct_clamps_to_tick_capped (line 2522) | Pure clamp. | BA-10 | LOW |
bucket_arb/mod.rs::t_autosell_invalid_pct_returns_none (line 2532) | Pure validation. | BA-10 | LOW |
bucket_arb/mod.rs::t_mark_basket_posted_transitions_tracking_to_posted (line 2558) | Pure FSM transition. | BA-6 | MEDIUM |
bucket_arb/mod.rs::t_mark_basket_posted_noop_on_already_participating (line 2580) | Pure idempotency. | BA-22 | LOW |
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-21 | LOW |
bucket_arb/mod.rs::t_st02_sum_hits_one_after_rounding_rejected (line 2701) | Pure rounding boundary. | BA-4 | LOW |
bucket_arb/mod.rs::t_st03_touched_book_rejected (line 2730) | Pure spec. | BA-4 | LOW |
bucket_arb/mod.rs::t_st04_missing_ask_phantom_gamma_rejected (line 2760) | Pure phantom-gamma rejection. | BA-2 | LOW |
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-21 | LOW |
bucket_arb/mod.rs::t_st07_one_sided_book_admits_via_ask_minus_tick (line 2877) | Pure one-sided book. | BA-21 | LOW |
bucket_arb/mod.rs::t_st08_profit_too_low_after_rounding (line 2915) | Pure rounding-induced edge. | BA-4 | LOW |
bucket_arb/mod.rs::t_st09_crossed_book_rejected (line 2955) | Pure crossed-book rejection. | BA-2 | LOW |
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
| Family | File range | ~Test count | Net Bucket B disposition |
|---|---|---|---|
| Directional pure evaluators | domain/strategies/directional.rs | 128 (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 ledger | application/order_manager.rs | 99 | ~99 B (25 MEDIUM, 70 LOW, 4 DELETE) |
| Bucket-arb FSM/admission | domain/strategies/bucket_arb/{mod,fsm,scanner_hooks,decision_log}.rs | 113 (68+18+25+2) | ~95 B (30 MEDIUM rewrite-pair-with LIV-12, 60 LOW, 5 DELETE) |
| Maker-rebate FSM | domain/strategies/maker_rebate.rs | 55 | ~50 B (15 MEDIUM pair LIV-4.*, 30 LOW, 5 DELETE) |
| Maker pure | domain/strategies/maker.rs | 38 | ~36 B (10 MEDIUM, 25 LOW, 1 DELETE) |
| Merge-arb FSM | domain/strategies/merge_arb.rs | 19 | ~18 B (8 MEDIUM pair LIV-5/40, 10 LOW) |
| DCA extension | domain/strategies/extensions/dca.rs | 24 | ~24 B (10 MEDIUM pair LIV-3, 14 LOW) |
| Strategy shared math | domain/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 advisor | domain/liquidity.rs | 49 | ~49 B (LOW; needs new L- rule group OR delete* if signal informational) |
| Market cache | domain/market_cache.rs | 16 | ~16 B (LOW or DELETE — internal cache) |
| WS book parser | io/ws/book.rs | 35 | ~35 B (LOW; consider proptest for external-contract drift) |
| WS user parser | io/ws/user.rs | 9 | ~9 B (LOW; same class) |
| RTDS parse pure | io/rtds/parse.rs (less 4 in Bucket A) | ~13 B | LOW |
| RTDS subscribe pure | io/rtds/subscribe.rs (less 3 in Bucket A) | ~7 B | LOW |
| RTDS metrics pure | io/rtds/metrics.rs | 7 | ~7 B (MEDIUM — chain assert needed) |
| RTDS smoke | io/rtds/smoke.rs | 3 | ~3 B (MEDIUM — should be live-network probe) |
| RTDS client | io/rtds/client.rs | 4 | ~4 B (MEDIUM) |
| Webhook signal/metrics | io/webhook/{signal,metrics}.rs | 14 | ~14 B (LOW) |
| Gamma client | io/gamma_client.rs | 22 | ~22 B (LOW; external-contract relevant) |
| Native balance | io/native_balance.rs | 3 | ~3 B (LOW) |
| Mock CLOB | io/clob/simulator/mock_clob.rs (less 5 in Bucket A) | ~7 B | LOW |
| Dry-run extras | io/clob/dry_run.rs (less 6 in Bucket A) | ~8 B | LOW |
| Persistence schema | persistence/db/schema.rs | 21 | ~21 B (LOW retain as DB regression) |
| Persistence queries | persistence/db/queries.rs | 28 | ~28 B (LOW retain) |
| Persistence writer | persistence/db/writer.rs | 27 | ~27 B (LOW retain) |
| Wallets DB extras | persistence/wallets.rs (less 5 in Bucket A) | ~23 B | LOW |
| Strategy config DB extras | persistence/strategy_config.rs (less 4 in Bucket A) | ~30 B | LOW |
| Journal GC | persistence/journal/gc.rs | 10 | ~10 B (LOW) |
| Journal mod/reader | persistence/journal/{mod,reader}.rs (less 4+5 in Bucket A) | ~0 B | (all in Bucket A) |
| Capture redact/sink/writer | persistence/capture/{redact,sink,writer}.rs | 10 | ~10 B (LOW) |
| Position store | persistence/position_store.rs | 4 | ~4 B (LOW) |
| Auto-prune | application/auto_prune.rs | 6 | ~6 B (LOW) |
| Dispatcher | application/dispatcher.rs | 4 | ~4 B (LOW; bind to G-5) |
| Scanner extras | application/scanner.rs (less 4 in Bucket A) | ~1 B | LOW |
| Alerts engine | application/alerts/engine.rs | 6 | ~6 B (LOW; partial Bucket A overlap) |
| Alerts rule extras | application/alerts/rule.rs (less 8 in Bucket A) | ~13 B | MEDIUM (other rules) |
| Alerts sender/sink/alert/metrics_tap | application/alerts/{sender,sink,alert,metrics_tap}.rs | ~11 B | LOW or DELETE |
| Action executor | application/action_executor.rs | 6 | ~6 B (LOW) |
| Capture / backtest harness | application/{capture,backtest/*}.rs | ~20 | ~20 B (LOW retain as backtest regression) |
| Engine extras | application/engine.rs (less 5 in Bucket A) | ~2 B | LOW |
| Executor extras | application/executor.rs (less 22 in Bucket A) | ~3 B | LOW |
| Domain timestamped/stats/contexts/decision/repository | domain/{timestamped,stats,strategies/{contexts,decision,repository}}.rs | ~15 | ~15 B (mostly DELETE candidates or LOW) |
| Config | config/{env,loader,mod,rtds,webhook,capture,validate,strategy/mod}.rs | ~31 | ~16 B (15 fail-fast tests promote to A — see audit note above) |
| Presentation | presentation/tui.rs | 3 | ~3 B (LOW) |
| Observability | observability/{logger,metrics}.rs | 14 | ~14 B (LOW or DELETE) |
| pm-common | pricing.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-sdk | clob/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-analytics | sweep/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-ID | Feature | Why no liveness test exists | Hypothesis: code bug or test gap | Priority | Cross-ref TD-NN |
|---|---|---|---|---|---|
| LIV-1.1 | RTDS WebSocket connect | No 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. | MEDIUM | TD-35 |
| LIV-1.2 | RTDS subscribe wire frame | tests/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. | MEDIUM | TD-35 |
| LIV-1.5 | Toxic-flow sliding window | domain/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. | MEDIUM | TD-35 |
| LIV-1.8 | RTDS metrics counter projection | tests/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. | HIGH | TD-33 (already exists per existing audit recommendation) |
| LIV-2.3 | Webhook idempotency dedup signal_id | webhook/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. | LOW | TD-36 |
| LIV-2.4 | IP allowlist enforcement | webhook/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.5 | Strategy scope mismatch routing | No 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. | MEDIUM | TD-35 |
| LIV-3.1 | Periodic timer fires at BOT_FALLBACK_SCAN_SECS | No 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. | LOW | TD-35 |
| LIV-3.2 | MarketCache iteration in evaluate_periodic | No test asserts that periodic tick visits every cache row. | Code likely correct; test gap. | LOW | TD-35 |
| LIV-3.4 | TimezoneCache used by evaluate_periodic | Spec 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. | HIGH | TD-37 (NEW) |
| LIV-4.3 | Paired maker-rebate Pending → OneFilled FSM | domain/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. | MEDIUM | TD-34 (existing) |
| LIV-4.4 | Paired BothFilled → Decision::CallMergePositions reaches executor | Per 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. | HIGH | TD-34 (existing) |
| LIV-4.5 | Imbalance timeout → paired_imbalance_escalation warn log | No 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. | MEDIUM | TD-34 (existing) |
| LIV-4.6 | Drift cancel on on_orderbook_update for paired | No test seeds OrderBook update and asserts MOC.cancel_calls.len() == 2. | Test gap. | MEDIUM | TD-34 (existing) |
| LIV-5.3 | merge-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). | HIGH | TD-34 (existing) |
| LIV-5.4 | merge-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. | HIGH | TD-34 (existing) |
| LIV-5.5 | merge-arb neg_risk kill-switch | MERGE_ARB_NEG_RISK_ENABLED=false admission filter not asserted via integration. | Test gap. | MEDIUM | TD-34 (existing) |
| LIV-6.2 | Recovery via on-chain CTF balance check | Spec 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. | MEDIUM | TD-38 (NEW) |
| LIV-6.4 | Recovery: no duplicate placement on first scan tick | tests/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. | HIGH | TD-39 (NEW) |
| LIV-6.5 | Recovery latency budget | Spec gap: production-readiness budget for recovery latency not numerically fixed. | Operational invariant; no SLO assertion. | LOW | (spec PR) |
| LIV-7.2 | Strategy-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. | MEDIUM | TD-40 (NEW) |
| LIV-9.3 | Directional evaluate_signal consults TZ cache for late-entry | Per 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. | MEDIUM | TD-34 (existing) |
| LIV-10.1 | BOT_STRATEGY=X ⇒ only X emits Decisions | application/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. | LOW | TD-41 (NEW) |
| LIV-10.2 | Cross-strategy if-branches absent | Compile-time enforced via Strategy trait; ast-grep gate not in CI. | Code likely correct; CI gap. | LOW | (CI rule add) |
| LIV-11.3 | Strategy-tagged client_id | executor.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. | LOW | TD-41 (NEW) |
| LIV-12.1 | on_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. | MEDIUM | TD-34 (existing) |
| LIV-12.2 | on_new_market → FSM Candidate→Tracking | tests/event_fsm_transitions.rs::t_register_candidate_on_new_market covers FSM mutation; doesn't drive through on_new_market hook. | Test gap; small. | LOW | TD-34 (existing) |
| LIV-12.3 | on_user_event (own-fill) routing | tests/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. | MEDIUM | TD-42 (NEW) |
| LIV-12.4 | on_market_resolved → Redeem decisions | executor.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. | MEDIUM | TD-42 (NEW) |
| LIV-13.4 | SSE 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. | MEDIUM | TD-34 (existing) |
| LIV-13.5 | Last-Event-ID replay (full backfill+live) | Same compile-only-shape gap. Status only asserted. | Test gap. | MEDIUM | TD-34 (existing) |
| LIV-13.7 | Hybrid 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. | MEDIUM | TD-43 (NEW) |
| LIV-14.1 | Single-row cancel — REST POST | tests/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. | MEDIUM | TD-34 (existing) |
| LIV-14.2 | Bulk cancel ≤5 — soft confirm | No test seeds 5 rows + asserts MOC.cancel_calls.len() == 5. | Test gap. | MEDIUM | TD-34 (existing) |
| LIV-14.3 | Bulk cancel >5 — paranoid CANCEL token | Per 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. | HIGH | TD-34 (existing) |
| LIV-14.4 | 5s soft-delete undo | Frontend store reducer test for the 5s undo branch — does not exist (per audit). | Frontend test gap; backend not affected. | LOW | TD-34 (existing) |
| LIV-14.5 | Cancel-reason taxonomy chip severity | Frontend severity-resolver test — does not exist. | Frontend test gap. | LOW | TD-34 (existing) |
| LIV-15.1 / LIV-15.2 / LIV-15.3 | Annualisation / risk-free / per-strategy slicing | Frontend Vitest tests for risk.ts. Per existing audit §14 ADR-014: claimed but unverified. | Test gap; math-only. | LOW | TD-44 (NEW) |
| LIV-16.2 | BOT_WALLET_LABEL set + empty WALLETS_MASTER_PASSPHRASE ⇒ panic | Per existing audit recommendation §10 ADR-015: the fail-fast INV-2 panic is not asserted by #[should_panic]. | Test gap; financial-system invariant. | HIGH | TD-34 (existing) |
| LIV-16.3 | INV-3 cross-check strategy.wallet_label == BOT_WALLET_LABEL | Same — INV-3 panic not asserted. | Test gap; financial-system invariant. | HIGH | TD-34 (existing) |
| LIV-16.4 | wallets.db AES-256-GCM round-trip | Bucket A covers basic round-trip. Decryption with wrong passphrase covered. Auth-tag forgery (modified ciphertext) NOT asserted. | Test gap on adversarial input. | LOW | TD-45 (NEW) |
| LIV-16.5 | Budget 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. | MEDIUM | TD-45 (NEW) |
| LIV-16.6 | config.db plaintext SELECT | No test asserts SELECT works with empty passphrase env. | Test gap; LOW. | LOW | TD-45 (NEW) |
| LIV-17.1 | WEBHOOK_ENABLED=true + missing SECRET ⇒ panic | Test 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). | MEDIUM | TD-46 (NEW) |
| LIV-17.2 | Non-loopback WEBHOOK_BIND_ADDR ⇒ warn (not panic) | No test captures the warn line via TestWriter and asserts startup proceeds. | Test gap. | LOW | TD-46 (NEW) |
| LIV-18.1 | --capture requires --dry-run | No CLI integration test asserts exit-code on --capture without --dry-run. | Test gap. | LOW | TD-47 (NEW) |
| LIV-18.3 | bot_sessions.mode enum extension | journal_migrations.rs::bot_sessions_mode_defaults_to_live asserts default; no assertion that mode='capture' or 'replay' rows survive round-trip. | Test gap; LOW. | LOW | TD-47 (NEW) |
| LIV-19.2 | DuckDB ATTACH aggregation | Per existing audit + feature-matrix: implementation queue claim; integration test absent. | Test gap. | LOW | TD-47 (NEW) |
| LIV-19.3 | What-if env-override CSV diff | pm-analytics/tests/whatif_integration.rs covers Bucket A but CSV column-content diff per LIV-19.3 boundary not asserted. | Partial test. | LOW | TD-47 (NEW) |
| LIV-20.1 | Watchdog reset on outbound ping success | RCA §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. | MEDIUM | TD-48 (NEW) |
| LIV-20.2 | Dashboard rtdsStatus() truthful indicator | RCA §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. | MEDIUM | TD-34 (existing) |
| LIV-21.1 | AST-grep rule blocks latest_tick.<counter> > 0 | .ast-grep/truthful-indicator.yml claimed; CI invocation not asserted. | CI rule gap. | LOW | TD-49 (NEW) |
| LIV-22.1 | --smoke-rtds daily CI cron | Required before next live trading session per RCA §6.2. Not yet a GitHub Actions cron. | Code+CI gap. P0 corrective. | HIGH | TD-33 (existing) — extended scope |
| LIV-22.2 | Polymarket API contract probe | RCA §6.4 P2; not yet implemented. | CI/code gap. | MEDIUM | TD-50 (NEW) |
| LIV-23.2 | Strategy-conditional surfacing after BOT_STRATEGY | config/mod.rs 10 tests; not asserted that BUCKET_ARB_* block is NOT validated under BOT_STRATEGY=directional. | Test gap on lazy validation invariant. | LOW | TD-46 (NEW) |
| LIV-23.4 | RTDS_* always-on + lazy gate knobs | Per CLAUDE.md Fail-fast — knobs missing under always-on policy panic. No #[should_panic] test for missing RTDS_TOXIC_FLOW_*. | Test gap. | MEDIUM | TD-46 (NEW) |
| LIV-25.1 | pm-analytics compact is Phase 4 stub | Spec 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.2 | Manual archival post-session | Operational, no test. | (operational) | LOW | (runbook only) |
| LIV-26.2 | pm-cli wallet remove paranoid DELETE token | No test asserts removal requires literal DELETE token. | Test gap. | LOW | TD-45 (NEW) |
| LIV-27.1 | Cancel reaches CLOB on toxic-flow active | tests/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. | MEDIUM | TD-32 (existing, extend) |
| LIV-27.2 | No cancel pile-up under repeated triggers | tests/rtds_active_cancel.rs::t_toxic_flow_isolates_per_condition_id (Bucket A) is closest; idempotency under repeated triggers explicitly NOT asserted. | Test gap. | MEDIUM | TD-32 (existing, extend) |
| LIV-28.1 | Pre-flight script runs all checks | RCA §6.4 P2: scripts/preflight/all.sh doesn't exist yet. | Code gap (P2). | LOW | TD-50 (NEW) |
| LIV-28.2 | Q3 timestamp invariant automated gate | RCA §5.1 + §6.2 — manual checklist item; needed as automated gate. | Code+CI gap. P0 corrective. | HIGH | TD-33 (existing, extend) |
| LIV-29.3 | cancel_all_orders(condition_id) reaches adapter | No test asserts MOC.cancel_all_calls recorded with cid. | Test gap. | LOW | TD-51 (NEW) |
| LIV-29.5 | get_balance reachable | No test asserts this trait method end-to-end (executor → SDK call). | Test gap. | LOW | TD-51 (NEW) |
| LIV-31.2 | SymbolicClock replaces Instant::now on replay | 15-callsite migration claimed; ast-grep gate enforcing 0 hits not in CI. | CI gap. | MEDIUM | TD-52 (NEW) |
| LIV-31.3 | StrategyRng 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. | MEDIUM | TD-52 (NEW) |
| LIV-32.1 | Exponential backoff 1s → 30s | Per existing audit: standard ws_client.rs reuse claimed. No test measures per-attempt delay. | Test gap. | LOW | TD-48 (NEW) |
| LIV-32.2 | Reconnect counter rate-limit | Covered indirectly via application/alerts/rule.rs::t_rtds_health_fires_on_reconnect_storm (Bucket A). | Already covered. | — | — |
| LIV-34.1 | 64 KB body cap → 413 | webhook/server.rs tests don't include 70KB POST → 413. | Test gap. | LOW | TD-46 (NEW) |
| LIV-36.1 | Idempotent consume by transactionHash | io/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. | LOW | TD-53 (NEW) |
| LIV-37.1 | MarketSnapshot.end_date field absent | Compile-time enforced + grep gate (CLAUDE.md). No test exercises grep gate in CI. | CI rule gap. | LOW | (CI rule add) |
| LIV-38.1 | All live tests use canonical wallet 0xBA561B30…7F4 | Operational invariant; not code-enforced. | Per memory feedback_live_test_wallet. | LOW | (operational) |
| LIV-39.1 | IERC1155.isApprovedForAll(owner, CTF) precheck | merge-arb-strategy.md §Decision 5; no #[should_panic] test for merge-arb startup with un-approved CTF. | Test gap. | MEDIUM | TD-54 (NEW) |
| LIV-39.2 | NegRiskAdapter approval precheck | Same; merge-arb startup with MERGE_ARB_NEG_RISK_ENABLED=true and un-approved adapter. | Test gap. | MEDIUM | TD-54 (NEW) |
| LIV-40.3 | Replenish on fill until BUDGET_PER_CID (merge-arb v2) | No test seeds N fills + asserts MOC.place_calls continues until cumulative ≥ budget. | Test gap. | MEDIUM | TD-55 (NEW) |
| LIV-42.1 | pm-cli cancel-all → POST per active order | No CLI integration test. | Test gap. | LOW | TD-56 (NEW) |
| LIV-42.2 | pm-cli sell-all | Same. | Test gap. | LOW | TD-56 (NEW) |
| LIV-43.1 / LIV-43.2 | Redeem standard / neg-risk via pm-cli | executor.rs::t_redeem_* (Bucket A) covers executor + adapter dispatch. CLI wrapper integration not asserted. | Test gap; downstream of Bucket A coverage. | LOW | TD-56 (NEW) |
| LIV-44.1 | pm-cli init --chain-id 137 writes .env | No CLI test. | Test gap. | LOW | TD-56 (NEW) |
| LIV-45.1 | pm-cli approve --with-wrap --wrap-all | Manual smoke 2026-05-01 (commit 871f811). No CI/integration. | Test gap. | LOW | TD-56 (NEW) |
| LIV-46.1 | RTDS 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. | MEDIUM | TD-50 (NEW) |
| LIV-47.1 | pm-dashboard /api/local/* returns 503 if pm-bot offline | Per existing audit gap §12 (b). No test boots dashboard alone and asserts 503 on /api/local/seed. | Test gap. | MEDIUM | TD-34 (existing) |
| LIV-48.1 | /{wallet} route works without pm-bot | No test boots dashboard alone with bot offline + Vue SPA loads. | Test gap (frontend). | LOW | TD-34 (existing) |
| LIV-49.1 | Diff-resubscribe on new active slugs | io/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. | MEDIUM | TD-35 (existing) |
| LIV-49.2 | Cleanup on FSM drain_cleanup_slug | No test seeds slug → terminal state and asserts outbound unsubscribe frame. | Test gap. | LOW | TD-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.mdbaseline, 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.rsinline 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 intests/rtds_active_cancel.rs+ 8 tests inapplication/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)
- 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. - 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. - 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)
- ADR-002 ScannerHooks per-hook E2E → LIV-12.1 still open in Bucket C.
- ADR-005 SSE wire-frame schema → LIV-13.4 still open (compile-only-shape via
tests/local_api.rs::stream_*). - ADR-011 paired maker-rebate executor wiring → LIV-4.4 still open.
- ADR-011
paired_imbalance_escalationwarn-log capture → LIV-4.5 still open. - ADR-012 dashboard truthfulness pattern → LIV-20.2 status unverified (
indicator-truthfulness.test.tsclaimed but uncommitted in worktree). - ADR-013 paranoid bulk-cancel gate → LIV-14.3 still open.
- ADR-015 INV-3 cross-check → LIV-16.3 still open.
- ADR-016 directional uses TZ at evaluate-signal → LIV-9.3 still open.
- merge-arb neg-risk vs standard CTF dispatch → LIV-5.3, LIV-5.4 still open.
Low-risk gaps
- ADR-007 layering lint → optional Tier-3 polish; not surfaced as LIV-ID.
- ADR-009 NullSink no-op proof → LIV-8.2 partial (replay parity infers).
- ADR-010 trait method coverage matrix → LIV-29.* — partial Bucket A coverage for required methods, gap on provided.
- directional periodic-scan cadence → LIV-3.1 still open.
Recommended TD entries (Phase 0 sign-off backlog)
Cross-referencing liveness-audit-2026-05-06.md §4 recommendations and this audit's findings:
| TD | LIV-IDs covered | Priority | Status |
|---|---|---|---|
| TD-31 | LIV-1.3 (parser ms+s), LIV-1.4 (parser→hook E2E) | HIGH | CLOSED in worktree by tests/rtds_hook_e2e.rs. Promote LIV-1.3/1.4 status to mapped. |
| TD-32 | LIV-1.6, LIV-1.7 (active-cancel) | HIGH | CLOSED in worktree by tests/rtds_active_cancel.rs. Promote to mapped. |
| TD-33 | LIV-1.8, LIV-1.9 (RTDS health AlertRule) + LIV-22.1 (daily CI cron) + LIV-28.2 (Q3 gate) | HIGH | PARTIALLY CLOSED: rule.rs covers 8 cases in worktree. Still open: GitHub Actions cron + Q3 gate. P0 per RCA §6.2. |
| TD-34 | bundle 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) | MEDIUM | OPEN — 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) | MEDIUM | NEW |
| TD-36 (NEW) | LIV-2.3 (webhook idempotency channel-depth combined assertion) | LOW | NEW |
| TD-37 (NEW) | LIV-3.4 (TZ deferred wiring at periodic path — ADR-016 §D5) | HIGH | NEW — code gap, not test gap |
| TD-38 (NEW) | LIV-6.2 (recovery via on-chain CTF balance) | MEDIUM | NEW — spec also gap |
| TD-39 (NEW) | LIV-6.4 (recovery no-dup placement on first scan tick) | HIGH | NEW |
| TD-40 (NEW) | LIV-7.2 (strategy-level dedup, post-restart, same cid) | MEDIUM | NEW |
| TD-41 (NEW) | LIV-10.1, LIV-11.3 (strategy isolation client_id prefix) | LOW | NEW |
| TD-42 (NEW) | LIV-12.3 (own-fill WS routing), LIV-12.4 (resolved → redeem decisions) | MEDIUM | NEW |
| TD-43 (NEW) | LIV-13.7 (hybrid PnL via /seed) | MEDIUM | NEW |
| TD-44 (NEW) | LIV-15.1/15.2/15.3 (frontend risk metrics math) | LOW | NEW |
| 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) | LOW | NEW |
| 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) | MEDIUM | NEW |
| TD-47 (NEW) | LIV-18.1, LIV-18.3 (capture session lifecycle), LIV-19.2/19.3 (sweep DuckDB ATTACH + whatif diff CSV) | LOW | NEW |
| TD-48 (NEW) | LIV-20.1 (watchdog ping-reset), LIV-32.1 (exponential backoff per-attempt) | MEDIUM | NEW |
| TD-49 (NEW) | LIV-21.1 (AST-grep CI rule for truthful indicators) | LOW | NEW |
| TD-50 (NEW) | LIV-22.2 (Polymarket API contract probe), LIV-28.1 (preflight script), LIV-46.1 (schema parity probe) | MEDIUM | NEW — corresponds to RCA §6.4 P2 |
| TD-51 (NEW) | LIV-29.3 (cancel_all), LIV-29.5 (get_balance) trait method coverage | LOW | NEW |
| TD-52 (NEW) | LIV-31.2 (SymbolicClock ast-grep), LIV-31.3 (StrategyRng determinism) | MEDIUM | NEW |
| TD-53 (NEW) | LIV-36.1 (cross-source dedup) | LOW | NEW |
| TD-54 (NEW) | LIV-39.1, LIV-39.2 (merge-arb approve preconditions panic) | MEDIUM | NEW |
| TD-55 (NEW) | LIV-40.3 (merge-arb v2 replenish-until-budget) | MEDIUM | NEW |
| 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) | LOW | NEW — 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 requirespnpmtest 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-ID | Boundary observed |
|---|---|---|---|
bucket_arb/scanner_hooks.rs::t_initial_setup_empty_cache_noop (line 1184) | YES | BA-2 (negative) | hook returns empty |
bucket_arb/scanner_hooks.rs::t_initial_setup_skipped_for_non_arb_strategy (line 1199) | YES | G-5, LIV-10.1 | hook returns empty under wrong strategy |
bucket_arb/scanner_hooks.rs::t_on_new_market_missing_slug_is_noop (line 1214) | YES | BA-2 (negative) | hook returns empty |
bucket_arb/scanner_hooks.rs::t_on_new_market_too_few_siblings_skips (line 1241) | YES | BA-3 (negative) | hook returns empty |
bucket_arb/scanner_hooks.rs::t_on_ws_book_tracked_basket_is_noop_state (line 1259) | YES | BA-13 | hook returns empty for tracked-state basket |
bucket_arb/scanner_hooks.rs::t_on_ws_price_unknown_asset_noop (line 1303) | YES | G-4 | hook returns empty for unknown asset |
bucket_arb/scanner_hooks.rs::t_on_user_event_unknown_order_noop (line 1321) | YES | LIV-12.3 (negative) | hook returns empty |
bucket_arb/scanner_hooks.rs::t_periodic_tick_abandons_stale_posted_basket_then_drains (line 1342) | YES | BA-12, LIV-3.1 | Decision::Cancel/Abandon emitted |
bucket_arb/scanner_hooks.rs::t_periodic_tick_keeps_abandoned_with_filled_legs (line 1389) | YES | BA-22 | Decision retention |
bucket_arb/scanner_hooks.rs::t_market_resolved_marks_basket_resolved_then_drains (line 1444) | YES | BA-17, LIV-12.4 | Decision::Redeem emitted per leg |
bucket_arb/scanner_hooks.rs::t_market_resolved_unrelated_basket_untouched (line 1501) | YES | BA-17 isolation | scoped emission |
bucket_arb/scanner_hooks.rs::t_on_ws_book_emits_post_basket_when_ready (line 1558) | YES | BA-3, BA-4, LIV-12.1 | Decision::PostBucketArbBasket |
bucket_arb/scanner_hooks.rs::t_on_ws_book_missing_book_on_sibling_leg_skips (line 1635) | YES | BA-2, BA-4 | hook returns empty |
bucket_arb/scanner_hooks.rs::t_on_ws_book_dedup_via_events_in_flight (line 1698) | YES | LIV-7.2 | hook dedup |
bucket_arb/scanner_hooks.rs::t_on_ws_book_immediate_fill_emits_post_only_false (line 1765) | YES | BA-7 (negative — taker mode), G-16 | post_only flag observed in Decision |
bucket_arb/scanner_hooks.rs::t_on_ws_book_immediate_fill_skips_when_margin_insufficient (line 1839) | YES | BA-4 negative | hook returns empty |
bucket_arb/scanner_hooks.rs::t_bucket_signal_registers_basket (line 1947) | YES | BA-26, LIV-2.6 | webhook → register |
bucket_arb/scanner_hooks.rs::t_bucket_arb_webhook_registers_same_as_gamma (line 1986) | YES | BA-26 parity | parity assertion |
bucket_arb/scanner_hooks.rs::t_market_signal_rejected_on_bucket_arb (line 2055) | YES | LIV-2.5 | strategy scope mismatch |
bucket_arb/scanner_hooks.rs::t_dedup_when_slug_already_tracking (line 2083) | YES | LIV-7.2 | dedup at slug level |
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_trade_marks_dirty (line 2205) | YES | BA-23, LIV-1.4 cousin | dirty flag set |
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_orders_matched_marks_dirty (line 2233) | YES | LIV-1.4 cousin | dirty flag set |
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_no_basket_no_op (line 2255) | YES | LIV-1.4 negative | no-op for unknown |
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_finalized_basket_skipped (line 2284) | YES | BA-22 | finalized skipped |
bucket_arb/scanner_hooks.rs::t_bucket_arb_rtds_non_arb_config_short_circuits (line 2317) | YES | G-5 | wrong-strategy short-circuit |
merge_arb.rs::t_watched_filter_in_band_admits (line 1206, async) | YES | MA-2 | admission observable |
merge_arb.rs::t_watched_filter_above_band_rejects (line 1252, async) | YES | MA-2 negative | rejection observable |
merge_arb.rs::t_watched_filter_lower_boundary_admits (line 1276, async) | YES | MA-2 boundary | admission |
merge_arb.rs::t_watched_filter_neg_risk_disabled_rejects (line 1299, async) | YES | LIV-5.5 | kill-switch admission filter |
merge_arb.rs::t_initial_setup_within_budget_emits_pair (line 1326, async) | YES | LIV-40.2 | symmetric BUY pair emitted (Decision shape) |
merge_arb.rs::t_initial_setup_over_budget_skips_emit (line 1354, async) | YES | MA-18 | budget gate |
merge_arb.rs::t_full_fill_yes_only_no_merge_replenish (line 1399) | YES | LIV-40.3 | replenish branch |
merge_arb.rs::t_balanced_inventory_emits_merge (line 1440) | YES | LIV-40.4 | merge Decision emit |
merge_arb.rs::t_replenish_skipped_when_budget_exhausted (line 1496) | YES | LIV-40.3 negative | budget exhaustion |
merge_arb.rs::t_band_exit_emits_cancel_merge_reconcile (line 1536, async) | YES | MA-8, LIV-40.5 | cancel+merge+reconcile sequence |
merge_arb.rs::t_band_exit_state_blocks_relayer (line 1594, async) | YES | MA-12 | reconcile blocks autoresolve |
merge_arb.rs::t_on_ws_book_pending_dedupe (line 1621, async) | YES | LIV-7.2 | pending dedup |
merge_arb.rs::t_on_ws_book_post_only_safety_skips_cross (line 1649, async) | YES | MA-16, G-16 | post_only safety |
merge_arb.rs::t_on_ws_book_re_arms_missing_side (line 1675, async) | YES | LIV-40.2 | re-arm |
merge_arb.rs::t_evaluate_periodic_stale_warn_touch (line 1719, async) | YES | G-28 | stale guard |
merge_arb.rs::t_evaluate_periodic_resolving_timeout_resets_idle (line 1750, async) | YES | MA-10 | timeout reset |
merge_arb.rs::t_evaluate_periodic_admits_new_watched (line 1784, async) | YES | MA-2 | admission via periodic |
merge_arb.rs::t_evaluate_periodic_drops_out_of_band_with_zero_inventory (line 1805, async) | YES | MA-2, MA-4 | drop on out-of-band |
merge_arb.rs::t_evaluate_periodic_keeps_out_of_band_with_nonzero_inventory (line 1827, async) | YES | MA-15 | residual reconcile |
maker.rs::t_profile_no_maker_equivalent (line 1100) | YES | MK-15, MK-16 | profile equivalence Decision shape |
maker.rs::t_profile_spread_maker_equivalent (line 1129) | YES | MK-14, MK-16 | profile equivalence |
maker.rs::t_profile_sniper_equivalent (line 1159) | YES | MK-13, MK-16 | profile equivalence |
maker.rs::t_skip_when_neg_risk (line 1199) | YES | MK-1 | scope filter |
maker.rs::t_skip_when_slug_pattern_mismatch (line 1209) | YES | MK-10 | slug filter |
maker.rs::t_admit_when_slug_pattern_matches_case_insensitive (line 1224) | YES | MK-10 | case-insensitive admit |
maker.rs::t_skip_when_probability_below_band (line 1240) | YES | MK-4, MK-9 | prob band lower |
maker.rs::t_skip_when_probability_above_band (line 1252) | YES | MK-4, MK-9 | prob band upper |
maker.rs::t_skip_when_bid_non_positive_after_safety (line 1264) | YES | MK-9 | bid-safety |
maker.rs::t_skip_when_spread_below_target (line 1282) | YES | MK-9 | spread filter |
maker.rs::t_skip_when_ask_above_max_price_relative_modes (line 1299) | YES | MK-7, MK-8 | max-price gate (relative modes) |
maker.rs::t_ask_above_max_price_ok_for_fixed_abs (line 1311) | YES | MK-6 | fixed-abs mode bypasses ask gate |
maker.rs::t_skip_when_midpoint_missing_relative_midpoint (line 1338) | YES | MK-7 | midpoint missing |
maker.rs::t_skip_when_size_below_one_share (line 1349) | YES | G-23, MK-9 | min-size |
maker.rs::t_on_full_fill_records_position (line 1379) | YES | G-26 | journal-shape |
maker.rs::t_on_full_fill_ignores_sell_fills (line 1402) | YES | MK-12 | hold-to-resolution; ignore Sell |
maker.rs::t_on_cancel_removes_unfilled_position (line 1421) | YES | MK-22 | cancel cleanup |
maker.rs::t_on_cancel_keeps_partially_filled_position (line 1442) | YES | MK-22 | partial-fill retention |
maker.rs::t_on_market_resolved_emits_redeem_and_clears (line 1463) | YES | MK-21, LIV-12.4 | redeem Decision |
maker.rs::t_on_market_resolved_ignores_unknown_condition (line 1507) | YES | MK-21 isolation | scoped 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) | YES | MK-6 | tick-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) | NO | MK-19 (gap) | DELETE candidate |
maker.rs::t_max_hours_to_expiry_outside_window_rejects (line 1647) | NO | MK-19 (gap) | DELETE candidate |
maker.rs::t_max_hours_to_expiry_fail_closed_when_deadline_unknown (line 1662) | NO | MK-19 (gap) | DELETE candidate |
maker.rs::t_max_hours_to_expiry_zero_skips_gate_even_without_deadline (line 1679) | NO | MK-19 (gap) | DELETE candidate |
maker.rs::t_maker_webhook_rejects_bucket_signal (line 1733, async) | YES | LIV-2.5 | strategy scope |
maker.rs::t_maker_webhook_no_config_noop (line 1759, async) | YES | LIV-2.6 | no-op without config |
maker.rs::t_maker_external_signal_market_in_cache_no_fetch (line 1876, async) | YES | LIV-2.6 | cache-hit |
maker.rs::t_maker_external_signal_market_not_in_cache_fetches_and_evaluates (line 1924, async) | YES | LIV-2.6 | cache-miss + fetch |
maker.rs::t_maker_external_signal_fetch_fails_returns_empty (line 1969, async) | YES | DIR-25 | error path |
maker.rs::t_maker_rtds_below_threshold_no_action (line 2079, async) | YES | G-12 negative | toxic-flow filter |
maker.rs::t_maker_rtds_dust_filtered_no_window_growth (line 2106, async) | YES | LIV-1.5 | dust filter |
maker.rs::t_maker_rtds_above_threshold_log_only_default_returns_empty (line 2132, async) | YES | G-13 negative | LOG_ONLY default |
maker.rs::t_maker_rtds_above_threshold_active_cancel_emits_cancels (line 2166, async) | YES | G-13, MK-20 | active-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-ID | Feature | Gap one-liner | Cross-ref TD |
|---|---|---|---|
| LIV-2.1 | Webhook server bind | TCP listener test exists in tests/e2e_webhook_smoke.rs; multi-port-collision behaviour not asserted | TD-46 |
| LIV-5.1 | bucket-arb accepts only neg_risk | Covered indirectly via tests/parallel_placement.rs::neg_risk_preresolved_passes_through_plan (Bucket A); explicit single-row test absent | TD-35 |
| LIV-5.2 | directional/maker/maker_rebate reject neg_risk | maker.rs::t_skip_when_neg_risk (Appendix A) covers maker; analogous tests for directional/maker_rebate not separately enumerated | TD-35 |
| LIV-6.1 | CLOB GET /orders restore live orders | tests/restart_attempt_recovery.rs (Bucket A) covers DB recovery; CLOB-side list_orders invocation count not directly asserted | TD-39 |
| LIV-6.3 | FSM reconstruction from event_log | tests/crash_recovery.rs (Bucket A) covers basket FSM; per-strategy state (RebatePair, InventoryFsm) not separately asserted | TD-39 |
| LIV-7.1 | HTTP-level dedup channel-depth combined | webhook/server.rs::t_returns_409_on_duplicate (Bucket A) covers HTTP; channel-depth gauge assertion absent | TD-36 |
| LIV-8.1 | Capture taps emit envelope | tests/capture_smoke.rs (Bucket A) covers; per-source-tag invariant for all 6 source-tags not exhaustive | (already mapped) |
| LIV-8.2 | NullSink no-op proof in production hot loop | Indirect via replay parity; explicit FS/metric assertion absent | TD-47 |
| LIV-9.1 | TZ activation predicate | Covered by timezone/mod.rs 13 unit tests (Bucket A indirect); strategy-side activation not asserted | TD-34 |
| LIV-9.2 | ICAO → local-day midnight | Covered by timezone/mod.rs; cross-DST boundary cases gap (per spec gap aggregate) | (spec PR) |
| LIV-11.1 | Decision::PlaceOrder reaches place_limit_order | mapped (tests/strategy_engine_integration.rs) | (mapped) |
| LIV-11.2 | Decision::CancelOrder reaches cancel_order | mapped | (mapped) |
| LIV-13.1 | Local-bot server bind | mapped (tests/local_api.rs) | (mapped) |
| LIV-13.2 | Disabled by default — bit-identical hot loop | Bucket A local_api.rs::bind_rejects_non_loopback_without_flag; explicit metric=0 assertion absent | TD-47 |
| LIV-13.3 | Atomic batch INV-A | mapped | (mapped) |
| LIV-13.6 | POST /rotate ROTATE token | mapped | (mapped) |
| LIV-15.1..15.3 | Frontend risk metrics | (Bucket C — TD-44) | TD-44 |
| LIV-16.1 | BOT_WALLET_LABEL unset ⇒ legacy .env path | wallets.rs::test_migrate_from_env_* (Bucket B) covers migration; runtime equivalence to legacy (metrics.wallet_source = "env") not asserted | TD-45 |
| LIV-17.3 | WEBHOOK_ENABLED=false ⇒ vacuous | mapped (tests/scanner_dispatch_test.rs::t_disabled_feature_is_vacuous) | (mapped) |
| LIV-18.2 | capture_sessions row inserted | Partial coverage via journal/mod.rs::t_run_migrations_installs_capture_sessions_table (Bucket A); insert-on-startup not E2E | TD-47 |
| LIV-19.1 | Sweep parallelism | Bucket A (pm-analytics/tests/sweep_integration.rs); SLO speedup not asserted | (mapped, partial) |
| LIV-21.1 | AST-grep convention rule | CI rule absent; truthful-indicator dashboard test claimed | TD-49 |
| LIV-22.1 | Daily smoke-rtds CI cron | HIGH P0 — already TD-33 extension | TD-33 |
| LIV-22.2 | Polymarket API contract probe | RCA §6.4 P2 | TD-50 |
| LIV-23.1 | BUCKET_ARB_* block missing ⇒ panic with full list | config/mod.rs 10 inline tests (Bucket B) cover parser; full-list panic content not asserted via #[should_panic] payload-content match | TD-46 |
| LIV-23.3 | WEBHOOK_* lazy validation | Indirect via t_disabled_feature_is_vacuous; not direct | TD-46 |
| LIV-24.1..24.3 | Optional defaults — no panic | config/mod.rs covers parser; runtime startup with unset defaults not E2E | TD-46 |
| LIV-25.1 | pm-analytics compact Phase 4 stub | Spec gap | (spec PR) |
| LIV-25.2 | Manual archival post-session | (operational) | (runbook only) |
| LIV-26.1 | pm-cli wallet rotate-master | Covered by wallets.rs::test_rotate_master_* (Bucket A) | (mapped) |
| LIV-26.2 | pm-cli wallet remove paranoid DELETE token | absent | TD-45 |
| LIV-27.1..27.2 | Cancel chain + no-pile-up | Partial via tests/rtds_active_cancel.rs (Bucket A); idempotent under-repeated-trigger explicit absent | TD-32 extend |
| LIV-28.1..28.2 | Pre-flight script + Q3 gate | absent / HIGH P0 | TD-50 + TD-33 |
| LIV-29.1..29.5 | OrderClient trait method coverage | Partial via Bucket A (LIV-29.1, 29.2 mapped); LIV-29.3..29.5 absent | TD-51 |
| LIV-30.1..30.2 | Strategy isolation invariants | Partial via application/dispatcher.rs::t_from_env_* (Bucket B) and compile-time enforcement | TD-41 |
| LIV-31.1 | Source-priority deterministic ordering | mapped (tests/replay_determinism.rs) | (mapped) |
| LIV-31.2 | SymbolicClock replaces Instant::now | ast-grep gate absent | TD-52 |
| LIV-31.3 | StrategyRng determinism | Partial via backtest/rng.rs (Bucket B); per-strategy seed across 10 replays not asserted | TD-52 |
| LIV-32.* | RTDS reconnect storm protection | Partial (LIV-32.2 covered by alerts/rule.rs); per-attempt delay measurement absent | TD-48 |
| LIV-33.1 | Webhook channel back-pressure | mapped (tests/scanner_dispatch_test.rs::t_full_channel_maps_to_503) | (mapped) |
| LIV-34.1 | Body-size cap → 413 | absent | TD-46 |
| LIV-35.1 | Token bucket 10 req/s burst 30 → 429 | mapped (webhook/server.rs::t_returns_429_when_burst_exhausted) | (mapped) |
| LIV-36.1 | RTDS dedup vs CLOB last_trade_price | Partial (io/rtds/dedupe.rs 8 tests Bucket A); cross-source dedup absent | TD-53 |
| LIV-37.1 | MarketSnapshot.end_date field absent | Compile-time + grep; CI rule explicit absent | (CI rule add) |
| LIV-37.2 | TimezoneCache canonical (covers LIV-9.*) | Partial via timezone/mod.rs | TD-34 |
| LIV-38.1 | Live test wallet 0xBA561B30…7F4 | Operational; non-runtime | (operational) |
| LIV-39.1..39.2 | Approve preconditions | absent | TD-54 |
| LIV-40.1..40.5 | Merge-arb v2 inventory FSM | Partial via Appendix A promotions (merge_arb.rs::t_* 19 tests); LIV-40.3 replenish-until-budget E2E absent | TD-55 |
| LIV-41.1..41.3 | Bucket-arb FSM invariants | mapped via 13 integration tests (Bucket A) | (mapped) |
| LIV-42.1..42.2 | pm-cli cancel-all / sell-all | absent | TD-56 |
| LIV-43.1..43.2 | Redeem CLI + adapter dispatch | Partial via executor.rs::t_redeem_* (Bucket A); CLI integration absent | TD-56 |
| LIV-44.1 | pm-cli init writes .env | absent | TD-56 |
| LIV-45.1 | pm-cli approve --with-wrap | absent (manual smoke 2026-05-01) | TD-56 |
| LIV-46.1 | RTDS schema parity probe | Partial via tests/rtds_hook_e2e.rs (Bucket A); explicit golden-diff schema_parity flag absent | TD-50 |
| LIV-47.1 | pm-dashboard 503 if pm-bot offline | absent | TD-34 |
| LIV-48.1 | /{wallet} route works without pm-bot | absent (frontend) | TD-34 |
| LIV-49.1..49.2 | RTDS subscription dynamic rebuild | Partial via io/rtds/subscribe.rs::t_filter_set_diff_* (Bucket A); outbound WS frame on mock-server absent | TD-35 |
| LIV-50.1 | CI parity workflow | mapped (.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:
- "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). - "Borderline fail-fast tests in
config/" — ~15 inline tests inconfig/{webhook,rtds,validate,strategy/mod}.rsexercise 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. - "Dead-code DIR-7 / MK-19 tests" — 5 tests in
directional.rs::t_max_hours_to_expiry_*(lines 5586..5654) + 5 tests inmaker.rs::t_max_hours_to_expiry_*(lines 1618..1679). These assert that*_MAX_HOURS_TO_EXPIRYdoes NOT filter (per memorymax_hours_dead_code). They enshrine dead code rather than spec invariant. Classification: DELETE candidates afterBOT_MAX_HOURS_TO_EXPIRYfield removal (separate refactor PR, requires user approval perfeedback_no_autonomous_changes). - "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. - "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. - "Assertions on
Vec<Decision>length and shape" — most async strategy tests inbucket_arb/scanner_hooks.rs,merge_arb.rs,maker.rs,maker_rebate.rsend withassert_eq!(decisions.len(), N)andmatches!(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 viatests/strategy_engine_integration.rsfor 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). - "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 Atests/replay_determinism.rs; classified as Bucket B retain-and-bind. - "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 toEventEnvelopedoes 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. 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 repositoryBotEvent/EventEnvelopeIS the contract betweenpm-botandpm-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.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, branchfeat/spec-driven-coverage. Test inventory captured viagrep -rn "^\s*#\[(tokio::)?test\]"oncrates/{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.mdfor 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.mdafter explicit user approval.