Polytraders Dev Guide
internal
v3 spine Phase 1 · Shared contracts 9 demo-wired · 0 shadow-ready · 0 production-live · 100 pending · 109 total 15/33 infra tasks the plan status board
HomeBy LayerIntelligence4.12 RuleChangeMonitor

4.12 RuleChangeMonitor

Intelligence Signal Service Read-only PLANNED Spec started capital · Indirect P2 · Data normalisation pending stub

Detect and emit any post-listing edit to a market's resolution rule.

v3 readiness

Docs27/27
donehow scored
Impl0/15
pendinghow scored
Backtest0/4
pendinghow scored
Runtime0/8
pendinghow scored

A bot is done when all four scores are. What does done mean?

1. Bot Identity

LayerIntelligence  Intelligence
Bot classSignal Service
AuthorityRead-only
StatusPLANNED
ReadinessSpec started
Runs before
Runs after
Applies to
Default modeshadow_only
User-visibleAdvanced details only
Developer ownerPolytraders core

2. Purpose

Detect and emit any post-listing edit to a market's resolution rule.

3. Why This Bot Matters

  • Resolution rule edited under an open position

    If a market's rule changes after a strategy entered, the basis for that trade may no longer exist. Without monitoring, the position is held through the change without anyone on the team being told.

  • Cosmetic vs semantic edits not distinguished

    Many edits are punctuation or formatting; a few are substantive. Treating all edits the same either floods operators with false alarms or misses the one that matters.

  • Audit trail of rule history missing

    Compliance and dispute reviews require a full history of every rule change with timestamps. Polymarket's UI does not expose this; the monitor builds the record the team needs.

  • Strategies can't auto-pause on rule change

    A strategy that reads a rule-change event can decide to flatten or hold automatically. Without the event, the team is the only line of defence and must catch every edit by hand.

4. Required Polymarket Inputs

InputSourceRequired?Use
Active market resolution_rules and question text from Gamma APIgammaYesFetch current rule text for comparison against stored snapshot hashes.

5. Required Internal Inputs

InputSourceRequired?Use
KillSwitch active flagKillSwitchYesSuppress all emissions when KillSwitch is active.
Last known rule snapshots from PostgresPostgresYesBaseline for hash comparison to detect rule changes.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
poll_interval_s3009003600Seconds between full market rule snapshot comparison cycles.
max_markets_per_cycle5008001000Maximum number of active markets to compare per poll cycle to bound Gamma API load.

7. Detailed Parameter Instructions

poll_interval_s

What it means

Seconds between full market rule snapshot comparison cycles.

Default

{ "poll_interval_s": 300 }

Why this default matters

300 s provides timely detection of rule changes on live markets without overloading Gamma.

Threshold logic

ConditionAction
interval <= 300 sNormal
300–900 sWARN — reduced detection speed for rule changes
> 3600 sReject — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

if (p.poll_interval_s > p.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');

User-facing English

Resolution rules are monitored regularly to catch any changes to active markets.

max_markets_per_cycle

What it means

Maximum number of active markets to compare per poll cycle to bound Gamma API load.

Default

{ "max_markets_per_cycle": 500 }

Why this default matters

500 covers all active Polymarket markets without risking API rate limits.

Threshold logic

ConditionAction
count <= 500Normal
500–800WARN — approaching API rate limit
> 1000Reject — PARAMETER_CHANGE_REQUIRES_APPROVAL

Developer check

if (p.max_markets_per_cycle > p.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');

User-facing English

Rule monitoring is designed to scale with the number of active markets.

8. Default Configuration

{
  "bot_id": "intel.rulechangemonitor",
  "version": "0.1.0",
  "mode": "planned",
  "defaults": {
    "poll_interval_s": 300,
    "max_markets_per_cycle": 500
  },
  "locked": {
    "poll_interval_s": {
      "max": 3600
    },
    "max_markets_per_cycle": {
      "max": 1000
    }
  }
}

9. Implementation Flow

— not yet authored —

10. Reference Implementation

Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. IF/THEN/ELSE = decision. Translate directly to TypeScript, Python, Go, or Rust.

FUNCTION monitorRuleChanges():
  // 0. KillSwitch check
  IF FETCH internal.killswitch.status == ACTIVE:
    RETURN

  // 1. Fetch active markets from Gamma
  markets = FETCH gamma.GET('/markets?status=active')
  IF len(markets) == 0:
    EMIT WARN 'STALE_DATA'
    RETURN

  // 2. Compare against stored snapshots
  FOR m IN markets:
    stored = db.get(m.condition_id)
    IF stored IS NULL:
      db.set(m.condition_id, snapshot(m))
      CONTINUE

    // 3. Detect rule or question changes
    IF hash(m.resolution_rules) != stored.rules_hash:
      EMIT WARN 'RULECHANGEMONITOR_RULE_CHANGED'
      EMIT ObservationReport with change_type='resolution_rules'
    IF hash(m.question) != stored.question_hash:
      EMIT WARN 'RULECHANGEMONITOR_QUESTION_CHANGED'
      EMIT ObservationReport with change_type='question'

    // 4. Update snapshot
    db.set(m.condition_id, snapshot(m))

SDK calls used

  • gamma.GET('/markets?status=active')
  • internal.killswitch.status

Complexity: O(M) per poll cycle where M = active markets

11. Wire Examples

Input — what arrives on the wire

{
  "label": "Gamma market response with changed resolution rules",
  "source": "gamma",
  "payload": {
    "condition_id": "0xf1a2b30000000000000000000000000000000000000000000000000000000000",
    "resolution_rules": "Resolves YES if Coinbase BTC/USD close price on Dec 31 2026 is >= 100000. UPDATED: Binance price also accepted.",
    "timestamp_ms": 1746703000000
  }
}

Output — what the bot emits

{
  "label": "ObservationReport — resolution rule changed",
  "payload": {
    "report_id": "rep_rcm_0xf1a2_1746703000000",
    "trace_id": "trc_0xbeef0102030405060713",
    "bot_id": "intel.rulechangemonitor",
    "kind": "ObservationReport",
    "condition_id": "0xf1a2b30000000000000000000000000000000000000000000000000000000000",
    "change_type": "resolution_rules",
    "old_hash": "0xabcd1234",
    "new_hash": "0xefgh5678",
    "emitted_at_ms": 1746703005000
  }
}

12. Decision Logic

APPROVE

— not yet authored —

RESHAPE_REQUIRED

— not yet authored —

REJECT

— not yet authored —

WARNING_ONLY

— not yet authored —

13. Standard Decision Output

This bot returns a RiskVote object. See RiskVote schema.

{
  "report_id": "rep_rcm_0xf1a2_1746703000000",
  "trace_id": "trc_0xbeef0102030405060713",
  "bot_id": "intel.rulechangemonitor",
  "kind": "ObservationReport",
  "condition_id": "0xf1a2b30000000000000000000000000000000000000000000000000000000000",
  "change_type": "resolution_rules",
  "old_hash": "0xabcd1234",
  "new_hash": "0xefgh5678",
  "change_detected": true,
  "emitted_at_ms": 1746703005000
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
RULECHANGEMONITOR_RULE_CHANGEDWARNResolution rules for an active market changed since last snapshot.Emit ObservationReport with change_type=resolution_rules; notify strategy layer.The resolution conditions for this market have changed.
RULECHANGEMONITOR_QUESTION_CHANGEDWARNQuestion text for an active market changed since last snapshot.Emit ObservationReport with change_type=question; flag for manual review.The market question has been updated.
STALE_DATAWARNGamma API returned empty or error response during rule monitoring poll.Skip cycle; retain last snapshots; retry on next poll.
KILL_SWITCH_ACTIVEHARD_REJECTKillSwitch active; all RuleChangeMonitor emissions suppressed.Continue monitoring but suppress ObservationReport emissions.Rule change signals paused while trading is suspended system-wide.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_intel_rulechangemonitor_observations_emitted_totalcountercountchange_typeObservationReports emitted per change type (resolution_rules, question).
polytraders_intel_rulechangemonitor_rule_changes_totalcountercountTotal resolution rule change events detected.
polytraders_intel_rulechangemonitor_markets_monitoredgaugecountNumber of active markets currently being monitored for rule changes.

Alerts

AlertConditionSeverityRunbook
RuleChangeMonitorRuleChangedrate(polytraders_intel_rulechangemonitor_rule_changes_total[5m]) > 0warn#runbook-rulechangemonitor-rule-changed
RuleChangeMonitorStalerate(polytraders_intel_rulechangemonitor_markets_monitored[30m]) == 0warn#runbook-rulechangemonitor-stale

Dashboards

  • Grafana — Intelligence / RuleChangeMonitor rule-change event rate

16. Developer Reporting

{
  "bot_id": "intel.rulechangemonitor",
  "markets_checked": 312,
  "rule_changes_detected": 1,
  "question_changes_detected": 0,
  "gamma_response_ms": 850,
  "killswitch_active": false,
  "emitted_at_ms": 1746703005000
}

17. Plain-English Reporting

SituationUser-facing explanation
Strategy paused entry after rule change detectedThe resolution conditions for this market were updated. The system flagged this change and paused new entries until the update was acknowledged.
No rule change detected during routine monitoringAll active market rules were verified to be unchanged in the latest monitoring cycle.

18. Failure-Mode Block

main_failure_modeGamma API outage prevents rule comparison for active markets, causing strategy layer to operate with potentially outdated resolution rule awareness during the outage window.
false_positive_riskMinor non-substantive formatting changes (whitespace, punctuation) to rule text cause a rules_hash change and trigger a false rule-change alert.
false_negative_riskGamma API returns a cached stale response during a legitimate rule change, causing RuleChangeMonitor to miss the change until the cache expires.
safe_fallbackIf Gamma API is unavailable for > poll_interval_s * 2, emit STALE_DATA WARN and suspend rule comparisons. Retain all last known snapshots in Postgres. Resume on next successful poll.
required_dependenciesPolymarket Gamma API, KillSwitch active flag, Postgres for rule snapshot and audit log

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
GAMMA_API_DOWNBlock Gamma API for 30 minAutomatic on Gamma recovery; full comparison on first successful poll
RULE_CHANGE_INJECTIONModify resolution_rules in mock Gamma for a live condition_idAutomatic once change acknowledged; snapshot updated
KILL_SWITCH_ONSet killswitch.active=true during active monitoringAutomatic on KillSwitch reset

20. State & Persistence

Cold-start recovery

On cold start, reload all snapshots from Postgres; run full comparison on first cycle.

21. Concurrency & Idempotency

AspectSpecification
Execution modelsingle-threaded periodic batch poll
Max in-flight1
Idempotency keycondition_id + new_rules_hash
Per-call timeout (ms)20000
Backpressure strategyskip cycle if previous poll still in progress
Locking / mutual exclusionPostgres advisory lock on monitor_cycle key

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchSuppress emissions when KillSwitch is active.

Emits to (downstream consumers)

BotWhyContract
strat.all_strategies

Sibling bots (same OrderIntent)

External services

ServiceEndpointSLA assumedOn failure
Polymarket Gamma APIhttps://gamma-api.polymarket.com99.9% / 500 ms p99

23. Security Surfaces

Abuse vectors considered

  • Adversary manipulates Gamma API response to inject false rule-change signals
  • High-frequency rule updates flood RuleChangeMonitor with WARN events

Mitigations

  • Postgres audit log provides tamper-evident record of all rule changes
  • Alert thresholds prevent single-event paging from minor text edits

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldno
Aware of negative-risk marketsno
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesMonitors Gamma API for resolution rule and question text changes across active markets. Read-only. No order signing.

API surfaces declared

gammainternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation0.1.0
schema2
releasedNone
planned_releaseQ3-2026

Migration history

DateFromToReasonAction taken
2026-04-28n/av2-specSpec drafted post-CLOB-V2 cutover; bot not yet implementedDesigned against V2 schema (pUSD, builder codes, V2 EIP-712 domain)

26. Acceptance Tests

Unit Tests

TestSetupExpected result
Rule change detected and ObservationReport emittedrules_hash changes between two consecutive polls for the same condition_idObservationReport emitted with change_type=resolution_rules, old_hash, new_hash
No change: no ObservationReport emittedrules_hash identical between pollsNo ObservationReport; no alert
KillSwitch suppresses emissionkillswitch.active=true; rule change presentNo ObservationReport; KILL_SWITCH_ACTIVE logged

Integration Tests

TestExpected result
Rule change detected, emitted, consumed by strategy layer for re-evaluationStrategy receives ObservationReport with change_detected=true and affected condition_id
Gamma API down: STALE_DATA emitted; snapshots retained in PostgresSTALE_DATA WARN; all last-known snapshots preserved; monitoring resumes on recovery

Property Tests

PropertyRequired behaviour
RuleChangeMonitor never submits or signs ordersAlways true
Postgres audit log receives an entry for every detected rule changeAlways true

27. Operational Runbook

RuleChangeMonitor alerts on resolution rule or question changes. Any rule change during a live position must be reviewed immediately. Postgres audit log is the compliance record.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
RuleChangeMonitorRuleChangedIdentify condition_id from alert payload. Verify rule change on Polymarket.com. Notify strategy team if positions are open on this market.Intelligence pod lead immediately; market operations if change is unexpected
RuleChangeMonitorStaleCheck Gamma API health. Verify Postgres connectivity. Trigger manual resync if API is healthy.Intelligence pod lead if stale > 30 min

Manual overrides

  • force_resync — POST /internal/rulechangemonitor/resync to force full snapshot comparison on all active markets — After Gamma API recovery or suspected missed changes

Healthcheck

Endpoint: /internal/health/rulechangemonitor | Green: Last poll < 30 min ago AND Postgres reachable AND Gamma API returning 200 | Red: No poll for > 1 h OR Postgres unreachable

28. Promotion Gates

A bot does not advance to the next readiness state until every gate below is green. Gates are observable from production data — no subjective sign-off.

Promote to Shadow

GateHow measuredThreshold
Unit tests pass for rule change, question change, and KillSwitch suppressionCI test run100% pass

Promote to Limited live

GateHow measuredThreshold
Full snapshot comparison of 500 active markets completes in < 20 s on stagingIntegration testCompletion time < 20 s

Promote to General live

GateHow measuredThreshold
100% detection of synthetic rule changes over 7-day soakIntegration test log audit100% detection

29. Developer Checklist

Ready-to-ship score: 27/27 sections complete · 100%

RequirementStatus
Purpose defined✓ done
Required inputs listed✓ done
Parameters defined✓ done
Defaults defined✓ done
Warning thresholds defined✓ done
Hard thresholds defined✓ done
Safe fallback defined✓ done
Structured output defined✓ done
Developer log defined✓ done
Plain-English explanation✓ done
Unit tests defined✓ done
Integration tests defined✓ done
Property tests defined✓ done
Failure-mode block complete✓ done
Reference implementation pseudocode✓ done
Wire examples (input + output)✓ done
Reason codes listed✓ done
Metrics & logs defined✓ done
State & persistence defined✓ done
Concurrency & idempotency defined✓ done
Dependencies declared✓ done
Security surfaces declared✓ done
Polymarket V2 compatibility declared✓ done
Version & migration history declared✓ done
Operational runbook defined✓ done
Promotion gates defined✓ done
Failure-injection recipes defined✓ done