Skip to content

Phase 0 Sign-Off — Spec-Driven Coverage (2026-05-06) ​

Status: BLOCKING gate — User decision required before Phase 1 starts. Worktree: feat/spec-driven-coverage at /home/me/RustroverProjects/polymarket-engine-tests (forked from main @ commit 940e9ca). Trigger: User request 2026-05-06 — 100% spec-driven coverage of trading business logic; lessons from RTDS-incident-2026-05-06 (silent-broken class).

1. Phase 0 deliverables (in worktree) ​

ArtefactPathSizeState
Worktree branchfeat/spec-driven-coverage—created from main 940e9ca
pm-test-utils cratecrates/pm-test-utils/2985 LOC, 73 unit testsgreen; clippy -D warnings clean
Rule matrixdocs/operations/test-coverage-matrix.md271 lines, 172 rule-IDs (G/BA/DIR/MK/MR/MA)all unmapped (Phase 1 maps tests)
Liveness matrixdocs/operations/feature-liveness-matrix.md546 lines, 143 LIV-IDs16 mapped (incl. LIV-1.4, LIV-1.7 promoted post-TD-31/32 closure), 2 partial (LIV-1.3, LIV-1.9), 125 unmapped
Acceptance criteriadocs/operations/acceptance-criteria-trading.md934 lines, 10 AC (Gherkin)all unmapped
Test audit reportdocs/operations/test-audit-report.md1059 lines, Bucket A / B / Cdone
CI workflows.github/workflows/{coverage,mutation-nightly}.ymlcomplements existing parity.yml, rtds-smoke.ymlscaffolded
Helper scriptsscripts/{check-requirement-coverage,check-liveness-coverage}.py, scripts/replay-determinism.shstdlib only (no pip)executable

2. Audit summary (test-audit-report.md) ​

Workspace census: 1572 tests. Bucket distribution:

  • Bucket A — kept (~210 tests): observe a side-effect boundary (placement_log / journal row / SSE envelope / log line / metric counter) AND derived from a business rule. Promote LIV-IDs / rule-IDs to mapped.
  • Bucket B — rewrite candidates (~1230 tests): pure-function asserts without rule-ID binding (potential code-derived tautology). Of those: 280 MEDIUM rewrite priority, 830 LOW (math correctness — keep but bind), 120 DELETE candidates (dead code per memory max_hours_dead_code, removed-feature stubs, construction-only smoke).
  • Bucket C — silent-broken-suspect (47 LIV-IDs): feature claimed in ADR/FSD but no existing test observes the boundary. Distribution: 8 HIGH, 24 MEDIUM, 15 LOW.

3. HIGH-risk silent-broken candidates (escalation) ​

8 HIGH-risk LIV-IDs requiring per-row User decision (test gap → write liveness test in Phase 2 cross-cutting; OR suspected code bug → separate trading PR).

LIV-IDFeatureHypothesisTD ref
LIV-3.4Directional evaluate_periodic consults TimezoneCacheCode gap — ADR-016 §D5 explicitly defers StrategyDeps wiring; directional.rs:1913 has TODO. Periodic harvester may misclassify "today vs tomorrow" markets per memory feedback_max_hours_expiry.TD-37 NEW
LIV-4.4Paired maker-rebate BothFilled → Decision::CallMergePositions reaches executorTest gap; possible silent-broken on first MAKER_REBATE_PAIRED_ENABLED=true activation. Only Vec<Decision> length asserted; executor wiring MOC.merge_calls.len() == 1 not.TD-34 existing
LIV-5.3merge-arb standard CTF dispatch (neg_risk=false)Test gap; financial-system risk per memory project_neg_risk_audit (5+ rounds of neg-risk inversion bugs). Decision-shape asserted but adapter dispatch not.TD-34 existing
LIV-5.4merge-arb NegRiskAdapter dispatch (neg_risk=true)Test gap; MERGE_ARB_NEG_RISK_ENABLED=false default → branch has zero coverage. HIGH RISK on first activation.TD-34 existing
LIV-6.4Recovery: no duplicate placement on first scan tickTest gap; dup placement = financial loss. tests/restart_attempt_recovery.rs covers attempt counter, doesn't directly assert MOC stays empty for N ticks post-recovery.TD-39 NEW
LIV-14.3Paranoid bulk-cancel CANCEL token gate (>5 rows)Test gap; operator-protective gate may be silently disabled. Backend integration test for 6-row cancel without token does not exist.TD-34 existing
LIV-16.2BOT_WALLET_LABEL set + empty WALLETS_MASTER_PASSPHRASE ⇒ panicTest gap; financial-system invariant. #[should_panic] test for INV-2 fail-fast does not exist.TD-34 existing
LIV-16.3INV-3 cross-check strategy.wallet_label == BOT_WALLET_LABEL ⇒ panicSame — INV-3 panic not asserted. Wallet-budget aliasing across strategies = silent over-allocation.TD-34 existing

4. CLOSED in worktree since 2026-05-06 RCA ​

Three RCA gaps from liveness-audit-2026-05-06.md §3 already addressed in main:

TDStatusClosure artefact
TD-31CLOSEDtests/rtds_hook_e2e.rs (3 tests, Bucket A) — RTDS parser → ScannerHooks::on_rtds_trade end-to-end
TD-32CLOSEDtests/rtds_active_cancel.rs (4 tests, Bucket A) — toxic-flow active-cancel branch
TD-33PARTIALLY CLOSEDapplication/alerts/rule.rs::t_rtds_health_* (8 tests, Bucket A). Still open: LIV-22.1 daily smoke-rtds CI cron + LIV-28.2 Q3 timestamp magnitude automated gate (P0 per RCA §6.2).

rtds-smoke.yml exists with daily 06:00 UTC cron — verify enabled in repo settings.

5. P0 still OPEN (RCA §6.2 must close before next live trading) ​

  • LIV-22.1 — daily --smoke-rtds CI cron actually wired into rtds-smoke.yml and active in repo. Check that the workflow is not in disabled state.
  • LIV-28.2 — Q3 timestamp-magnitude invariant as automated gate (not just SmokeReport pass condition) — timestamp_format != Milliseconds MUST fail the build, not just emit a warning.

6. TD entries to merge into tech-debt-backlog.md ​

26 entries surfaced. Cross-mapping with existing liveness-audit-2026-05-06.md recommendations:

TDSourcePriorityLIV-IDsStatus
TD-31existing auditHIGHLIV-1.3, 1.4CLOSED in worktree
TD-32existing auditHIGHLIV-1.6, 1.7CLOSED in worktree
TD-33existing audit, extendedHIGH (P0 outstanding)LIV-1.8, 1.9, 22.1, 28.2PARTIALLY CLOSED — extend
TD-34existing audit, bundleMEDIUM13 LIV-IDs (see audit §Cross-mapping)OPEN
TD-35..TD-56this audit (NEW)mixed (HIGH×2, MEDIUM×11, LOW×9)30+ LIV-IDsOPEN

Detail of NEW TDs in test-audit-report.md §"Recommended TD entries". User sign-off required per memory feedback_no_autonomous_changes before any test PR lands against these TDs.

7. Decision points (User must answer) ​

D1. Phase 0 sign-off: approve / reject?

  • Approve ⇒ Phase 1 P0 starts (merge-arb gap closure: 3 unit → ~80 spec-driven, e2e replay, MA-001/MA-005 fixtures).
  • Reject ⇒ which artefact to revisit (matrix, audit, signoff body)?

D2. HIGH-risk gaps (LIV-3.4, LIV-4.4, LIV-5.3, LIV-5.4, LIV-6.4, LIV-14.3, LIV-16.2, LIV-16.3) — for each:

  • Test gap (write liveness in Phase 2)? — default for 6 of 8.
  • Suspected code bug → separate trading PR? — possible for LIV-3.4 (TODO acknowledged in code) and possibly LIV-5.4 (zero coverage on activation branch suggests untested code).

D3. P0 RCA outstanding: confirm rtds-smoke.yml scheduled cron is active in repo settings; confirm Q3 invariant gate works as a exit 1 not just SmokeReport text.

D4. TD-31/32/33 closure verification — re-run on main branch to confirm tests/rtds_hook_e2e.rs + tests/rtds_active_cancel.rs + t_rtds_health_* are green; promote LIV-1.3..1.9 to mapped in matrices.

D5. Worktree merge cadence:

  • (a) PR per phase (recommended — Phase 0 deliverables as one PR, Phase 1 P0 merge-arb as next, etc.).
  • (b) Single big PR at end (more reviewer load, slower iteration).
  • (c) Continue work directly in worktree without merging until Phase 4 done (highest risk of merge conflict with active main work).

D6. Bucket B rewrite/delete decisions — 120 DELETE candidates and 280 MEDIUM rewrites need per-file owner approval. Phase 1 P0 will surface concrete file lists for sign-off.

8. What Phase 0 deliberately did NOT do ​

Per spec-driven principle (memory feedback_spec_driven_tests):

  • No new test written (test-PR scope = Phase 1+).
  • No production code modified (any code change = separate trading PR).
  • No reclassification of existing tests in matrices (rule-ID / LIV-ID test refs stay TBD until Phase 1 mapping).
  • No frontend Vitest audit (out of scope per test-audit-report.md §"Out of scope").
  • No mutation-testing pass (Phase 3 will run cargo-mutants nightly).
  • No live-network probe re-run (RCA §6.4 daily Polymarket contract probe is design-phase).

9. Verification commands (run from worktree) ​

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

# pm-test-utils green
cargo test -p pm-test-utils                                                  # 73 passed
cargo clippy -p pm-test-utils --all-targets -- -D warnings                   # zero warnings

# matrix parsers reachable + ratchet baselines (see coverage.yml)
python3 scripts/check-requirement-coverage.py \
    docs/operations/test-coverage-matrix.md \
    --allow-unmapped 172                                                     # 0/172 mapped → exit 0 at Phase 0 baseline
python3 scripts/check-liveness-coverage.py \
    docs/operations/feature-liveness-matrix.md \
    --allow-unmapped 127                                                     # 16 mapped (14 baseline + LIV-1.4 + LIV-1.7), 2 partial (LIV-1.3, LIV-1.9), 125 strict unmapped; parser counts partial as unmapped → 127 ≤ 127 → exit 0

# replay determinism (existing parity gate)
./scripts/replay-determinism.sh bucket-arb directional maker maker-rebate    # merge-arb skipped: no fixture yet

# RCA-class regression sanity (TD-31/32/33 closure verification)
cargo test -p pm-bot --test rtds_hook_e2e                                    # 3/3 — TD-31
cargo test -p pm-bot --test rtds_active_cancel                               # 4/4 — TD-32
cargo test -p pm-bot --lib t_rtds_health_                                    # 8/8 — TD-33 unit-side
                                                                             # NB: --lib bypasses integration-test compile;
                                                                             # use `--no-run --tests` separately when needed
cargo test -p pm-bot --tests --no-run                                        # all integration tests compile

The --lib form for TD-33 is intentional: cargo test -p pm-bot t_rtds_health_ (without --lib) attempts to compile every integration target first, which would historically fail on the two ManagedOrder test initializers in tests/manual_cancel_attempt.rs:49 and tests/bucket_arb_no_exit_sell.rs:207 (missing rationale / book_snapshot fields added to domain/order_lifecycle/types.rs:90 on 2026-05-06). Both initializers were patched in this Phase 0 worktree; cargo test -p pm-bot --tests --no-run now compiles clean.

10. Review-finding fixes (post-initial sign-off, this revision) ​

User review of the initial sign-off (2026-05-06) surfaced 3 BLOCKERs + 1 HIGH + 2 MEDIUM. Each addressed in this revision:

#SeverityFindingResolution
1BLOCKERcoverage.yml ran the matrix parsers without --allow-unmapped, so any PR fails at 0/172 + 14/143.coverage.yml now sets REQUIREMENT_UNMAPPED_BASELINE=172 and LIVENESS_UNMAPPED_BASELINE=127 as ratchet env vars; gate fails only when unmapped EXCEEDS baseline. PRs that map an ID drop the baseline by exactly 1 in the same commit.
2BLOCKERfeature-liveness-matrix.md still listed LIV-1.4 / 1.7 / 1.9 as unmapped despite TD-31/32/33 having been closed in worktree.LIV-1.4 → mapped (tests/rtds_hook_e2e.rs::*); LIV-1.7 → mapped (tests/rtds_active_cancel.rs::*); LIV-1.9 → partial (alerts/rule.rs::tests::t_rtds_health_*, with CI cron LIV-22.1 + Q3 hard-gate LIV-28.2 still open per RCA §6.2); LIV-1.3 → partial (magnitude detection covered by parse.rs tests). Liveness baseline is now 127 (from 129), reflecting the +2 net mapping.
3BLOCKERcargo test -p pm-bot t_rtds_health_ failed at compile time due to missing rationale + book_snapshot on two ManagedOrder initializers in integration-test files. Verification command misleading.Both initializers patched (tests/manual_cancel_attempt.rs:67 and tests/bucket_arb_no_exit_sell.rs:226 now set rationale: None, book_snapshot: None). §9 verification commands rewritten to use --lib for the TD-33 unit case and --tests --no-run for the integration compile sanity, with the dependency between them documented.
4HIGHpm-test-utils boundary recorders are scaffold-grade: JournalTail::from_path returns an empty in-memory tail; RecordingOrderClient::list_orders/get_order/get_balance forward without recording.Documented as Phase 0 deliberate scope: "MVP scaffold for liveness scenarios that need book/event/decision boundaries; SQLite journal tail and full per-method recording are Phase 1 follow-ups". Tracked as TD-57 (NEW). Production paths in pm-test-utils (OrderBookBuilder, RecordingOrderClient::place/cancel/cancel_all/merge, MetricsSnapshot) are complete and exercised by 73 unit tests.
5MEDIUMmutation-nightly.yml ran cargo mutants ... || true — informational, not a gate.Workflow rewritten: continue-on-error: true on the mutants steps tolerates exit code, then a Python step parses outcomes.json, computes caught / (caught + missed + timeout), and sys.exit(1) on miss. Initial floor 60% (Phase 0 starting baseline); ratchet to 85% (domain/strategies/**) and 90% (pm-common::{pricing,neg_risk}) over Phase 3.
6MEDIUMQ3 timestamp-magnitude hard-gate lives in the workflow, not in the binary — pm-bot --smoke-rtds returns Ok(()) regardless of timestamp_format.Documented authoritative gate location: .github/workflows/rtds-smoke.yml:133 parses SmokeReport text and exit 1 on timestamp_format != Milliseconds OR schema_parity != true OR trades_parsed < 1. Decision deferred to User: lift the gate into main.rs::smoke_rtds (return non-zero when any pass-condition fails) OR keep at workflow layer with periodic operator review. Tracked as TD-58 (NEW).

After these fixes, the worktree is internally consistent: matrices, ratchet env vars, sign-off verification commands, and the actual workspace state agree.

11. References ​

  • docs/operations/incident-2026-05-06-rtds.md — RCA, P0/P1/P2 corrective actions.
  • docs/operations/liveness-audit-2026-05-06.md — original 18-ADR audit, 16 ranked gaps.
  • docs/operations/test-audit-report.md — this Phase 0 audit, 1572 tests classified.
  • docs/operations/test-coverage-matrix.md — 172 rule-IDs.
  • docs/operations/feature-liveness-matrix.md — 143 LIV-IDs.
  • docs/operations/acceptance-criteria-trading.md — 10 Gherkin AC.
  • ~/.claude/plans/qa-lead-agent-system-analyst-agent-nifty-globe.md — approved Phase 0..4 plan.
  • Memory entries: feedback_silent_feature_broken, feedback_spec_driven_tests, feedback_worktree_isolation.