Тема
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
| Artefact | Path | Size | State |
|---|---|---|---|
| L1 spec-derived units | crates/pm-bot/tests/spec_merge_arb_unit.rs | 51 active passing + 17 #[ignore] (68 entries) | green |
| L2 FSM scenarios | crates/pm-bot/tests/spec_merge_arb_fsm.rs | 7 active passing + 23 #[ignore] (30 entries) | green |
| L3 synthetic replay | crates/pm-bot/tests/e2e_merge_arb_replay.rs | 1 passing (10× SHA-256 determinism) + 5 #[ignore] (6 entries) | green |
| L3 fixture contract | crates/pm-bot/tests/fixtures/synthetic/merge_arb/NOTE.md | 5 fixture specs (MA-006..010) | scaffolded |
| Matrix sync | docs/operations/test-coverage-matrix.md | MA-1..28 rows updated | 14 mapped + 2 partial + 12 unmapped |
| Liveness sync | docs/operations/feature-liveness-matrix.md | LIV-5.3, 5.4, 5.5 → partial | partial |
| Coverage ratchet | .github/workflows/coverage.yml | REQUIREMENT_UNMAPPED_BASELINE: 172 → 158 (-14) | dropped |
| pm-bot-events scaffolding | crates/pm-bot-events/tests/wire_format.rs | 4 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]requiresVec<Decision>slice inspection from inside strategy). - MA-27 — slug field round-trip mapped; filter logic deferred (
#[ignore]lives in private functionwatched_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 ID | Blocking | Fix shape |
|---|---|---|
SPEC_GAP_NEW | MergeArbStrategy::new_for_test constructor not pub-reexported | Add 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_ENTRY | Strategy trait hook signatures + HookCtx not in allowed-read set | Same lib-facade — re-export domain::strategies::trait_def::Strategy + HookCtx. |
SPEC_GAP_FSM_PROBE | No public accessor for in-flight InventoryFsm discrete state | Add #[cfg(test)] pub fn fsm_state(&self, cid: &str) -> Option<InventoryFsm> to MergeArbStrategy (or expose via journal event tail). |
SPEC_GAP_RUNTIME_INV | No 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 # zero6. 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 DefaultonMetricsTick(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 --captureoperator session this week to unfreeze MA-006..010 fixtures (and unblock 5 e2e tests + LIV-5.3/4/5 promotion tomapped)? - Or accept
partialLIV-5.* status indefinitely (financial-system risk per memoryproject_neg_risk_audit)?
7. What Phase 1 P0 deliberately did NOT do
- Read any
domain/strategies/*.rswhile authoring tests (spec-driven principle, memoryfeedback_spec_driven_tests). - Modify production code (memory
feedback_no_autonomous_changes). - Add
pm-test-utilstopm-bot[dev-dependencies]— the agent constraint "edit only the new test file" prevented the dep wire-up. Tests use inlined builder helpers shape-compatible withpm_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.