Skip to content

Phase 1 P0 Sign-Off — Merge-Arb Gap Closure (2026-05-06) ​

Status: BLOCKING gate — User decision required before Phase 1 P1 (directional/maker/maker-rebate top-up) starts. Worktree: feat/spec-driven-coverage at /home/me/RustroverProjects/polymarket-engine-tests. Main untouched. Trigger: Phase 0 sign-off approved 2026-05-06 (phase-0-signoff-2026-05-06.md); Phase 1 P0 = merge-arb gap closure (3 unit → spec-derived target ~80) per approved plan.

1. Deliverables ​

ArtefactPathSizeState
L1 spec-derived unitscrates/pm-bot/tests/spec_merge_arb_unit.rs51 active passing + 17 #[ignore] (68 entries)green
L2 FSM scenarioscrates/pm-bot/tests/spec_merge_arb_fsm.rs7 active passing + 23 #[ignore] (30 entries)green
L3 synthetic replaycrates/pm-bot/tests/e2e_merge_arb_replay.rs1 passing (10× SHA-256 determinism) + 5 #[ignore] (6 entries)green
L3 fixture contractcrates/pm-bot/tests/fixtures/synthetic/merge_arb/NOTE.md5 fixture specs (MA-006..010)scaffolded
Matrix syncdocs/operations/test-coverage-matrix.mdMA-1..28 rows updated14 mapped + 2 partial + 12 unmapped
Liveness syncdocs/operations/feature-liveness-matrix.mdLIV-5.3, 5.4, 5.5 → partialpartial
Coverage ratchet.github/workflows/coverage.ymlREQUIREMENT_UNMAPPED_BASELINE: 172 → 158 (-14)dropped
pm-bot-events scaffoldingcrates/pm-bot-events/tests/wire_format.rs4 sites patched (MetricsTick × 2, OrderPlaced, OrderSnapshot × 2)green

Test totals across P0:

  • 59 active passing tests (51 L1 + 7 L2 + 1 L3 determinism)
  • 45 #[ignore] with structured spec-gap reasons (17 L1 + 23 L2 + 5 L3 fixture-pending)
  • 0 failures in new tests

2. Coverage promotions ​

MA-* mapped (14 rule-IDs) ​

MA-1, MA-2 (incl. proptest), MA-3, MA-4, MA-5, MA-13, MA-14, MA-15, MA-16, MA-18 (incl. proptest), MA-19, MA-22, MA-24 (replay determinism), MA-26.

MA-* partial (2) ​

  • MA-17 — cancel-reason field mapped via t_ma_17_*; ordering observation deferred (#[ignore] requires Vec<Decision> slice inspection from inside strategy).
  • MA-27 — slug field round-trip mapped; filter logic deferred (#[ignore] lives in private function watched_markets_filter).

MA-* unmapped (12) — deferred to Phase 1 P1+ ​

MA-6, MA-7, MA-8, MA-9, MA-10, MA-11, MA-12, MA-20, MA-21, MA-23, MA-25, MA-28.

All 12 require either the strategy public-facade gaps documented in §3 or live capture sessions.

LIV-5.* (merge-arb financial-system risk) ​

  • LIV-5.3 standard CTF dispatch — partial (wire-shape pinned via L1 + L2; e2e #[ignore] pending MA-008 fixture).
  • LIV-5.4 NegRiskAdapter dispatch — partial (wire-shape pinned; e2e #[ignore] pending MA-006 fixture).
  • LIV-5.5 neg-risk kill-switch — partial (FSM #[ignore] pending operator-revert facade; e2e #[ignore] pending MA-009 fixture).

Net liveness baseline impact: 0 (parser counts partial as unmapped). Liveness ratchet baseline stays 127 per coverage.yml.

3. Spec gaps surfaced (4 distinct, blocking 23+ tests) ​

L2 FSM scenario tests are blocked on these public-facade gaps. Each #[ignore] cites which gap blocks it:

Gap IDBlockingFix shape
SPEC_GAP_NEWMergeArbStrategy::new_for_test constructor not pub-reexportedAdd pub use domain::strategies::merge_arb::MergeArbStrategy; (or test-only new_for_test) to pm-bot/src/lib.rs (test-only pub via #[cfg(any(test, feature = "test-utils"))]).
SPEC_GAP_HOOK_ENTRYStrategy trait hook signatures + HookCtx not in allowed-read setSame lib-facade — re-export domain::strategies::trait_def::Strategy + HookCtx.
SPEC_GAP_FSM_PROBENo public accessor for in-flight InventoryFsm discrete stateAdd #[cfg(test)] pub fn fsm_state(&self, cid: &str) -> Option<InventoryFsm> to MergeArbStrategy (or expose via journal event tail).
SPEC_GAP_RUNTIME_INVNo public accessor for running merge-arb inventory (filled-yes / filled-no / pending sentinel)Same — #[cfg(test)] pub fn inventory(&self, cid: &str) -> Option<InventorySnapshot>.

Lifting any one gap unblocks the listed tests verbatim. Module-level //! doc in spec_merge_arb_fsm.rs lists priority order. Each fix is ~5-10 LOC of test-only re-exports/accessors and does not change production behaviour — it just lifts the public surface.

4. Findings escalated (separate trading PRs) ​

F-1: pre-existing test failure unrelated to merge-arb ​

crates/pm-bot/tests/metrics_loop.rs::metrics_loop_projects_rtds_counters_when_wired — assertion Subscribed→2: left=Some(3) right=Some(2). Tracks incident-2026-05-06-rtds.md §6.4 P2 ("rtds_connection_state stuck at 2 post-fix (not 3) — investigate whether Polymarket dropped the SUBSCRIBE ack frame entirely or changed its shape"). The state machine has drifted from spec; the choice is to either fix the production code to land on 2 or simplify the FSM to merge 2+3 into a single Subscribed state. Out of P0 scope; needs separate trading PR per memory feedback_no_autonomous_changes.

F-2: pm-bot-events test scaffolding drift ​

Six #[test] sites in crates/pm-bot-events/tests/wire_format.rs were missing 14 MetricsTick additive fields (capture writer health × 4, strategy selectivity × 10) and 2 OrderPlaced/OrderSnapshot fields (rationale, book_snapshot). Same drift class as the Phase 0 ManagedOrder patches. Patched in this worktree (test-only scaffolding fix; analogous to Phase 0 §10 line-item #3). Recommend a separate trading-side PR to land an impl Default on MetricsTick (or a metrics_tick_with_defaults() test helper) so the next field bump doesn't cascade.

F-3: synthetic fixtures pending live capture ​

5 e2e replay tests are scaffolded #[ignore] against fixture contracts in tests/fixtures/synthetic/merge_arb/NOTE.md. Recommended path: capture one prod session via pm-bot --strategy merge-arb --dry-run --capture --capture-path .tmp/captures/ against an evergreen 50/50 binary (politics primary, sports pre-match), then derive MA-006..010 by hand-editing frame sequences (NOTE.md path 1). Live captures MA-001 (golden) and MA-005 (neg-risk merge through NegRiskAdapter) deferred to operator session per Phase 0 plan.

5. Verification commands ​

bash
cd /home/me/RustroverProjects/polymarket-engine-tests

# Full workspace test (one known failure escalated as F-1)
cargo test --workspace                                                       # 1 failed (F-1), all merge-arb green

# Merge-arb-only — clean
cargo test -p pm-bot --test spec_merge_arb_unit                              # 51 passed, 17 ignored
cargo test -p pm-bot --test spec_merge_arb_fsm                               # 7 passed, 23 ignored
cargo test -p pm-bot --test e2e_merge_arb_replay                             # 1 passed (determinism), 5 ignored

# Coverage gates
python3 scripts/check-requirement-coverage.py docs/operations/test-coverage-matrix.md --allow-unmapped 158
python3 scripts/check-liveness-coverage.py docs/operations/feature-liveness-matrix.md --allow-unmapped 127

# Phase 0 baselines confirmed
cargo test -p pm-test-utils                                                  # 73 passed
cargo clippy -p pm-test-utils --all-targets -- -D warnings                   # zero

6. Decision points (User must answer) ​

D1. Approve Phase 1 P0 sign-off?

  • Approve ⇒ Phase 1 P1 starts (directional / maker / maker-rebate top-up).
  • Reject ⇒ which deliverable revisit?

D2. Spec-gap unblocking strategy (Phase 1 P0.7 if approved):

  • Add 4 lib-facade re-exports (5-10 LOC each, test-only, zero production change). This unblocks 23 FSM scenarios → ~30 more MA-* mappings without writing new tests.
  • Or defer until P1 strategies hit the same wall; address all 5 strategies' facade gaps in one PR.

D3. F-1 RCA-related test failure (metrics_loop_projects_rtds_counters_when_wired):

  • Fix in production code (state machine landing on 2, or merge 2+3 per RCA §6.4)? Separate trading PR.
  • Or temporarily #[ignore] until the RCA P2 lands?

D4. F-2 MetricsTick::Default trading-side cleanup:

  • Land impl Default on MetricsTick (eliminates this scaffolding drift class)? Separate trading PR.
  • Or accept the precedent of patching test sites per field bump?

D5. F-3 Live capture for MA-001 / MA-005:

  • Run a pm-bot --dry-run --capture operator session this week to unfreeze MA-006..010 fixtures (and unblock 5 e2e tests + LIV-5.3/4/5 promotion to mapped)?
  • Or accept partial LIV-5.* status indefinitely (financial-system risk per memory project_neg_risk_audit)?

7. What Phase 1 P0 deliberately did NOT do ​

  • Read any domain/strategies/*.rs while authoring tests (spec-driven principle, memory feedback_spec_driven_tests).
  • Modify production code (memory feedback_no_autonomous_changes).
  • Add pm-test-utils to pm-bot [dev-dependencies] — the agent constraint "edit only the new test file" prevented the dep wire-up. Tests use inlined builder helpers shape-compatible with pm_test_utils::*. Migration to upstream re-exports is a near-rename when the dep is wired.
  • Build live MA-001 / MA-005 fixtures (deferred per plan; needs operator capture session).

8. References ​

  • Phase 0 sign-off: docs/operations/phase-0-signoff-2026-05-06.md
  • Approved plan: ~/.claude/plans/qa-lead-agent-system-analyst-agent-nifty-globe.md
  • RCA: docs/operations/incident-2026-05-06-rtds.md
  • Existing audit: docs/operations/liveness-audit-2026-05-06.md
  • Test audit: docs/operations/test-audit-report.md
  • Matrix: docs/operations/test-coverage-matrix.md
  • Liveness: docs/operations/feature-liveness-matrix.md
  • AC: docs/operations/acceptance-criteria-trading.md
  • L3 fixture contracts: crates/pm-bot/tests/fixtures/synthetic/merge_arb/NOTE.md
  • Memory: feedback_spec_driven_tests, feedback_silent_feature_broken, feedback_no_autonomous_changes, feedback_worktree_isolation.