Skip to content

fix(collector): bound Polymarket reference trade state - #680

Merged
proerror77 merged 3 commits into
mainfrom
codex/pm-reference-bounded-state
Aug 4, 2026
Merged

fix(collector): bound Polymarket reference trade state#680
proerror77 merged 3 commits into
mainfrom
codex/pm-reference-bounded-state

Conversation

@proerror77

@proerror77 proerror77 commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Change contract

Remove the obsolete persisted global Polymarket trade-ID map, cap, and eviction path. Track bounded per-market full-snapshot identity, emit a settled market’s trades once only after lag plus consecutive stable snapshots, and retain only bounded active-tape recovery IDs so restarts cannot refetch already-written rows.

Issue relationship

Refs #589

Out of scope

Production deployment or restart; OSS publication/readback; Polymarket market-tape collector; uploaders; Binance Spot or USDM collectors.

Dependencies and merge order

None

Focused validation

  • cargo test -p hft-collector --locked polymarket_raw::tests::collector_preserves_partial_trade_ids_across_two_restarts — 1 passed
  • cargo test -p hft-collector --locked polymarket_raw::tests:: — 84 passed, 2 ignored
  • cargo test -p hft-collector --locked --bin polymarket-raw-ops — 7 passed
  • cargo clippy -p hft-collector --locked --lib --bin polymarket-raw-ops --no-deps -- -D warnings
  • rustfmt --edition 2021 --check on both changed files
  • git diff --check; managed-worktree preflight; exact two-file scope

Counterexamples cover legacy retained-ID state removal, stable settlement before emission, bounded active-tape recovery, and preservation across two consecutive restarts without duplicate emission.

Rollout and rollback

No automatic production action. The runtime-labeled #589 remains open until its separately controlled production survival and readback acceptance evidence exists. Runtime restart and independent OSS data/manifest/_SUCCESS readback require a separately named controller and gate after merge. Roll back by reverting this commit before any runtime restart.

Scope exception

Approved by the user/coordinator for this atomic two-file collector safety contract. The line threshold is crossed because the obsolete persisted-ID state, cap, eviction implementation, callers, CLI option, and dead tests are deleted end-to-end while focused snapshot/recovery coverage replaces them; splitting would leave a known duplicate or crash path.

Summary by CodeRabbit

  • Bug Fixes

    • Improved recovery after interruptions during trade collection.
    • Prevented incomplete or duplicate trade snapshots from being finalized.
    • Added safer handling for state limits and tape rotation.
    • Removed legacy trade-ID retention behavior and related configuration.
  • Tests

    • Expanded coverage for snapshot finalization, recovery, migration, rotation, and legacy-state removal.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@proerror77, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bd9002a1-e329-4e6c-a61e-65bc928e08bf

📥 Commits

Reviewing files that changed from the base of the PR and between 7a6a29d and 5d901c2.

📒 Files selected for processing (2)
  • rust_hft/tools/collector/src/bin/polymarket-raw-ops.rs
  • rust_hft/tools/collector/src/polymarket_raw.rs
📝 Walkthrough

Walkthrough

Changes

Polymarket snapshot recovery

Layer / File(s) Summary
Configuration and state contracts
rust_hft/tools/collector/src/bin/polymarket-raw-ops.rs, rust_hft/tools/collector/src/polymarket_raw.rs
Removed max_retained_trade_ids. Added bounded snapshot state and recovery-only trade IDs.
Snapshot collection and completion
rust_hft/tools/collector/src/polymarket_raw.rs
Validated and deduplicated trade snapshots. Delayed emission until stable post-settlement snapshots. Bound completion records to snapshot counts and ID digests.
Recovery and tape rotation
rust_hft/tools/collector/src/polymarket_raw.rs
Rebuilt recovered trade IDs, deferred rotation while recovery remained incomplete, and rejected finalization without required completion records. Updated migration, recovery, rotation, and snapshot tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the collector change to bound Polymarket reference trade state.
Description check ✅ Passed The description includes all required template sections and provides clear scope, validation, rollout, rollback, and exception details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/pm-reference-bounded-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proerror77
proerror77 force-pushed the codex/pm-reference-bounded-state branch from 21372ce to 63581cb Compare August 4, 2026 04:01

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21372ce759

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust_hft/tools/collector/src/polymarket_raw.rs
Comment thread rust_hft/tools/collector/src/polymarket_raw.rs
Comment thread rust_hft/tools/collector/src/polymarket_raw.rs Outdated
@proerror77
proerror77 force-pushed the codex/pm-reference-bounded-state branch from 63581cb to 4ebfa97 Compare August 4, 2026 04:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
rust_hft/tools/collector/src/polymarket_raw.rs (1)

2846-2851: 📐 Maintainability & Code Quality | 🔵 Trivial

Expose recovery state in the health payload.

The health output dropped the trade-ID eviction fields and reports no replacement. Deferred rotation is now an invisible state: the active tape stops rotating while recovery_pending is true, and operators see only a growing active_tape_bytes. Add the unresolved recovery counts so alerting can detect a stalled rotation before the byte cap fails the cycle.

📈 Suggested health fields
             "malformed_trade_rows": malformed_trade_reasons.values().sum::<u64>(),
             "malformed_trade_reasons": malformed_trade_reasons,
+            "recovery_pending": self.writer.recovery_pending,
+            "recovered_trade_conditions": self.state.recovered_trade_ids.len(),
+            "recovered_trade_ids": self.state.recovered_trade_ids.values().map(BTreeSet::len).sum::<usize>(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust_hft/tools/collector/src/polymarket_raw.rs` around lines 2846 - 2851,
Update the health payload construction near the existing trade poll and
malformed-row fields to expose the unresolved trade-ID eviction/recovery counts,
including the recovery-pending replacement state used by deferred rotation.
Reuse the existing recovery counters from the surrounding collector logic so
operators can detect stalled rotation alongside active_tape_bytes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust_hft/tools/collector/src/polymarket_raw.rs`:
- Around line 1479-1487: Update retain_requested_market_state to also remove
recovered_trade_ids entries whose condition_id belongs to a market pruned from
state.markets, while preserving entries for retained markets. Use the market
condition identifiers available in CollectorState and keep the existing return
value based on whether tracked market state changed.
- Around line 1995-2008: Update trade_completion_update to merge
snapshot.record_ids with state.recovered_trade_ids for the given condition_id
before computing trade_count and trade_record_ids_sha256. Use this union for the
completion digest so recovered trade rows remain represented when absent from
the current API snapshot.
- Around line 2800-2802: In the recovery flow around updates.replay, preserve
the existing recovery_pending value while replaying the batch so pending hour
rotation cannot occur before recovered rows are durable. Move the assignment
derived from next_state.recovered_trade_ids to after updates.replay succeeds,
before persisting the updated state, while retaining the current replay and
error-propagation behavior.

---

Nitpick comments:
In `@rust_hft/tools/collector/src/polymarket_raw.rs`:
- Around line 2846-2851: Update the health payload construction near the
existing trade poll and malformed-row fields to expose the unresolved trade-ID
eviction/recovery counts, including the recovery-pending replacement state used
by deferred rotation. Reuse the existing recovery counters from the surrounding
collector logic so operators can detect stalled rotation alongside
active_tape_bytes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1e21da04-c05b-405f-8fe6-0746b3eb5e11

📥 Commits

Reviewing files that changed from the base of the PR and between a738b51 and 7a6a29d.

📒 Files selected for processing (2)
  • rust_hft/tools/collector/src/bin/polymarket-raw-ops.rs
  • rust_hft/tools/collector/src/polymarket_raw.rs

Comment thread rust_hft/tools/collector/src/polymarket_raw.rs
Comment thread rust_hft/tools/collector/src/polymarket_raw.rs Outdated
Comment thread rust_hft/tools/collector/src/polymarket_raw.rs
@proerror77
proerror77 force-pushed the codex/pm-reference-bounded-state branch from 7a6a29d to 7586937 Compare August 4, 2026 06:06
@proerror77
proerror77 merged commit 112d2b2 into main Aug 4, 2026
53 checks passed
@proerror77
proerror77 deleted the codex/pm-reference-bounded-state branch August 4, 2026 06:30
proerror77 added a commit that referenced this pull request Aug 7, 2026
…polls (#743)

The data API paginates a live trade feed (offset 0 and 10000), so the two
pages routinely overlap and report the same trade twice. Since #680 the
collector rejected any poll containing a duplicate record id as malformed;
on real traffic that marked hundreds of markets failed, tripped the
stale_after fail-closed exit, and the host shadow gate correctly refused
the candidate.

A duplicate record id is a SHA-256 over the full trade identity, so the
repeated row is content-identical to the one already kept. Deduplicate it
(exactly what the pre-#680 code did) and surface the count as a dedicated
duplicate_trade_rows health metric instead of a malformed reason, keeping
the poll fail-closed for genuinely malformed rows. This also restores
compliance with the gate policy malformed_trade_rows == 0 contract.

Co-authored-by: Sonic Shih <sonic.shih@mandonothing.com>
proerror77 added a commit that referenced this pull request Aug 8, 2026
…seconds (#757)

* feat(polymarket-release): extend the shadow gate observation to 3600 seconds

Since #680 the collector emits trades only after settlement plus the
1800-second finalization lag plus stable polls, so a 900-second
observation always produced an empty shadow trade set and the gate's
trade coverage parity could never pass (issue #756). Extend the
production gate duration to 3600 seconds, matching the bybit and usdm
reference gates, so markets closing early in the observation finalize
before the parity cutoff. Policy keeps the one-second rounding
tolerance (3600-3601) and the 601-second parity tail.

* test(polymarket-release): align control-plane fixtures with the 3600-second gate

The health-age boundary acceptance case and the runtime budget assertion
encoded the 900-second duration; update the elapsed spans and the
computed budget (1200+3600+5520=10320) to the new observation length.

* fix(polymarket): cover deferred trade finalization in the parity trade window

Address the review on the original change: lengthening only the gate
observation still cannot make exact trade parity attainable, because
verify-shadow-parity retained legacy trades through cutoff-600 while the
collector emits trades only after settlement plus the 1800-second
finalization lag plus three stable polls (~2100 seconds after close).
Any 5/15-minute market closing inside (cutoff-1800, cutoff-600] leaves
legacy-only trades in the window.

The trade maturity lag becomes 2400 seconds (finalization 1800 + market
window 900 with poll/settlement margin) and moves from a hard-coded
constant into ShadowParityConfig, sourced from
DEFAULT_TRADE_MATURITY_LAG_SECONDS (2400 in production, 600 in unit
fixtures). verify-shadow-parity gains an optional
--trade-maturity-lag-seconds flag so mechanism tests keep their small
fixture epochs; the production gate never passes it, and the gate
policy pins metrics.trade_maturity_lag_seconds == 2400 plus the derived
window bindings.

Also re-span the affected control-plane fixtures: health-age boundary
cases to 3600 elapsed seconds with parity windows wider than the new
lag, the baseline health completion chain past the parity window end,
the delayed post-cutoff trade row past the new cutoff, the main gate
fixture's parity window end to 3000, and the stale-health fixture's
completion to 01:46:41 so its 2701-second age check stays the
discriminating predicate.

---------

Co-authored-by: Sonic Shih <sonic.shih@mandonothing.com>
proerror77 pushed a commit that referenced this pull request Aug 14, 2026
- initialize the finalization maxima once to null; an empty second
  assignment made the first jq fold die before parity
- classify collector emission mode by the collect-reference CLI contract:
  #680 removed --max-retained-trade-ids exactly when finalization-deferred
  emission replaced per-poll emission, so the probe binds to #680 rather
  than the later post-#743 health counter, and any probe uncertainty
  classifies continuous (fail-closed, full trade parity)
- require finalization advancement, not existence: a positive growing
  stable-poll maximum (zero until the 1800-second lag elapses) or a
  growing settled maximum, mirrored in the gate policy, with flat-counter
  forged evidence rejected and single-channel growth accepted in the
  control-plane contract tests

Refs #868
proerror77 added a commit that referenced this pull request Aug 14, 2026
…rity (#875)

* fix(deploy): adjudicate unsatisfiable Polymarket shadow gate trade parity

A finalization-deferred shadow (post-#680) emits a market's trades only
after settlement plus the 1800-second finalization lag plus stable polls,
so against a continuous-emission baseline no mature shadow trade can exist
inside the 3600-second gate window: the trade coverage/field/byte parity
trio is unsatisfiable by construction and every post-#680 legacy_overlap
gate has failed with rust_trade_count=0 despite green metadata,
settlement, rotation, asset, and dedupe parity.

Detect both emission modes from the collector health schema. In the mixed
case, replace the trio with equivalent-strength satisfiable evidence: the
remaining parity families must pass, the raw verifier failure must be
confined to the trade family, the shadow must emit no trade the baseline
lacks, the finalization pipeline must engage (tracked/settled/stable-poll
counters from the shadow state, eviction-safe running maxima), and the
canonical upload still runs. The verdict records the applied trade parity
mode, both emission modes, the raw verifier checks, and the reason.
Same-semantics gates keep full trade parity unchanged.

Closes #868

* fix(deploy): reword gate comment to satisfy Rust-only source enforcement

* fix(deploy): address shadow gate parity review threads

- initialize the finalization maxima once to null; an empty second
  assignment made the first jq fold die before parity
- classify collector emission mode by the collect-reference CLI contract:
  #680 removed --max-retained-trade-ids exactly when finalization-deferred
  emission replaced per-poll emission, so the probe binds to #680 rather
  than the later post-#743 health counter, and any probe uncertainty
  classifies continuous (fail-closed, full trade parity)
- require finalization advancement, not existence: a positive growing
  stable-poll maximum (zero until the 1800-second lag elapses) or a
  growing settled maximum, mirrored in the gate policy, with flat-counter
  forged evidence rejected and single-channel growth accepted in the
  control-plane contract tests

Refs #868

---------

Co-authored-by: Sonic Shih <sonic.shih@mandonothing.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant