{
  "schema_version": "1.0.0",
  "bot_id": "3.10",
  "bot_name": "Resolution Fair-Value",
  "slug": "resolution-fair-value",
  "layer": "Strategy",
  "layer_key": "strat",
  "bot_class": "Alpha Strategy",
  "authority": [
    "Trade"
  ],
  "status": "planned",
  "readiness": "Spec started",
  "flagship": false,
  "is_reference": false,
  "public_export": false,
  "identity": {
    "layer": "Strategy",
    "bot_class": "Alpha Strategy",
    "authority": "Trade",
    "runs_before": "Risk guardrail pipeline",
    "runs_after": "Resolution tracker / Oracle monitor",
    "applies_to": "Near-resolution Polymarket binary markets where the outcome is derivable from an authoritative source but the CLOB price has not yet converged to the fair value (typically \u00a299\u2013\u00a21)",
    "default_mode": "shadow_only",
    "user_visible": "Advanced details only",
    "developer_owner": "Polytraders core \u2014 Strategy pod"
  },
  "purpose": "Resolution Fair-Value detects Polymarket binary markets that are close to resolution and priced away from their fair value (e.g. trading at \u00a296 when the outcome is virtually certain). It emits an IOC OrderIntent to trade toward fair value on unambiguous markets whose oracle signal is clean and within resolution window.",
  "why_it_matters": [
    {
      "failure": "Ambiguous resolution rule",
      "consequence": "A market that appears certain may resolve 'N/A' or be disputed. Trading on assumed certainty creates a position that resolves against expectations."
    },
    {
      "failure": "Oracle data stale near resolution",
      "consequence": "Oracle snapshots have a freshness window; a stale snapshot near resolution may show apparent certainty when the underlying data has moved."
    },
    {
      "failure": "UMA dispute window not respected",
      "consequence": "A winning position can be disputed and reversed within the 2-hour challenge window. Ignoring open disputes creates timing risk."
    }
  ],
  "polymarket_inputs": [
    {
      "input": "CLOB mid-price and depth",
      "source": "ws_market",
      "required": true,
      "use": "Measure divergence from estimated fair value."
    },
    {
      "input": "Market metadata (resolution source, end time, status)",
      "source": "clob_public",
      "required": true,
      "use": "Verify oracle source is clean, resolution is imminent, and market is open."
    },
    {
      "input": "UMA dispute status",
      "source": "onchain",
      "required": true,
      "use": "Check for open disputes before trading near resolution."
    }
  ],
  "internal_inputs": [
    {
      "input": "KillSwitch active flag",
      "source": "KillSwitch",
      "required": true,
      "use": "Abort all intent emission if KillSwitch active."
    },
    {
      "input": "Oracle signal / resolution tracker output",
      "source": "internal (resolution tracker)",
      "required": true,
      "use": "Derive fair-value probability from authoritative resolution source."
    },
    {
      "input": "Builder code bytes32",
      "source": "internal config",
      "required": true,
      "use": "Injected into builder field on every signed V2 OrderIntent."
    }
  ],
  "raw_params": [
    "min_edge_bps \u00b7 int",
    "max_size_per_market_usd \u00b7 int (\u2264 2000)",
    "require_unambiguous_source \u00b7 bool",
    "require_oracle_clean \u00b7 bool"
  ],
  "parameters": [
    {
      "name": "min_edge_bps",
      "default": 100,
      "warning": 50,
      "hard": 20,
      "controls": "Minimum divergence in bps between CLOB price and estimated fair value required to emit an OrderIntent.",
      "why_default_matters": "100 bps provides margin after Polymarket fees (~50 bps) and residual uncertainty near resolution.",
      "threshold_logic": [
        {
          "condition": ">= 100 bps",
          "action": "EMIT IOC OrderIntent"
        },
        {
          "condition": "50\u2013100 bps",
          "action": "WARN RFV_EDGE_MARGINAL; emit at 50% size"
        },
        {
          "condition": "< 20 bps",
          "action": "SKIP \u2014 RFV_NO_EDGE"
        }
      ],
      "dev_check": "if edge_bps < params.hard: return skip('RFV_NO_EDGE')",
      "user_facing": "The market price was already too close to the estimated fair value."
    },
    {
      "name": "max_size_per_market_usd",
      "default": 500,
      "warning": 750,
      "hard": 1000,
      "controls": "Maximum pUSD exposure per market near resolution.",
      "why_default_matters": "500 pUSD limits single-market resolution risk.",
      "threshold_logic": [
        {
          "condition": "<= 500 pUSD",
          "action": "Normal sizing"
        },
        {
          "condition": "> 1000 pUSD",
          "action": "Reject config \u2014 PARAMETER_CHANGE_REQUIRES_APPROVAL"
        }
      ],
      "dev_check": "if params.max_size_per_market_usd > params.hard: raise ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL')",
      "user_facing": "Trade size was capped at the configured per-market maximum."
    },
    {
      "name": "require_unambiguous_source",
      "default": true,
      "warning": null,
      "hard": null,
      "controls": "If true (locked), only trade markets where the resolution rule parsing confidence is at maximum. Ambiguous rules block trading.",
      "why_default_matters": "Prevents trading on markets where resolution could be disputed or voided.",
      "threshold_logic": [
        {
          "condition": "ambiguous source detected",
          "action": "HARD_REJECT RFV_AMBIGUOUS_SOURCE"
        }
      ],
      "dev_check": "if not oracle_clean: return skip('RFV_AMBIGUOUS_SOURCE')",
      "user_facing": "The market's resolution rule was not fully clear; no trade was placed."
    },
    {
      "name": "require_oracle_clean",
      "default": true,
      "warning": null,
      "hard": null,
      "controls": "Requires oracle signal freshness and no open UMA dispute before emitting any OrderIntent.",
      "why_default_matters": "Stale oracle or open dispute creates timing risk near resolution.",
      "threshold_logic": [
        {
          "condition": "oracle stale or dispute open",
          "action": "HARD_REJECT RFV_ORACLE_NOT_CLEAN"
        }
      ],
      "dev_check": "if oracle_stale or dispute_open: return skip('RFV_ORACLE_NOT_CLEAN')",
      "user_facing": "The oracle data was not fresh or a dispute is open \u2014 no trade placed."
    }
  ],
  "default_config": {
    "bot_id": "strat.resolution_fair_value",
    "version": "0.1.0",
    "mode": "shadow_only",
    "defaults": {
      "min_edge_bps": 100,
      "max_size_per_market_usd": 500,
      "require_unambiguous_source": true,
      "require_oracle_clean": true
    },
    "locked": {
      "require_unambiguous_source": {
        "value": true
      },
      "require_oracle_clean": {
        "value": true
      },
      "min_edge_bps": {
        "min": 20
      }
    }
  },
  "implementation_flow": [
    "Check KillSwitch; if active, emit no OrderIntents.",
    "FETCH clob_public market status; skip if closed or resolved.",
    "Check oracle signal from resolution tracker; if stale or source ambiguous, emit RFV_ORACLE_NOT_CLEAN / RFV_AMBIGUOUS_SOURCE and skip.",
    "Check onchain UMA dispute status; if dispute open, skip.",
    "Compute fair_value from oracle signal; edge_bps = |fair_value - clob_mid| * 10000.",
    "IF edge_bps < hard (20 bps): emit sampled DecisionReport RFV_NO_EDGE; skip.",
    "IF edge_bps < warning (50 bps): WARN RFV_EDGE_MARGINAL; reduce size 50%.",
    "Compute order size = min(max_size_per_market_usd, available_depth).",
    "EMIT IOC OrderIntent toward fair value; EMIT DecisionReport reason=RFV_EDGE_TRADE."
  ],
  "decision_logic": {
    "approve": "edge_bps >= min_edge_bps, oracle clean, no dispute, unambiguous source, market open, KillSwitch inactive.",
    "reshape_required": "Not applicable \u2014 reshaping handled by downstream Risk guardrail.",
    "reject": "edge_bps < 20 bps; oracle stale; dispute open; ambiguous source; KillSwitch active.",
    "warning_only": "edge_bps between 20 and 50 bps triggers warning and 50% size reduction."
  },
  "decision_output_schema": "OrderIntent",
  "decision_output_example": {
    "intent_id": "oi_01HRFV0000001A",
    "trace_id": "tr_01HRFV000TR001",
    "market_id": "0xrfv0000000000000000000000000000000000000000000000000000000000001",
    "outcome": "YES",
    "side": "buy",
    "price": "0.960",
    "size_pUSD": "300.00",
    "tif": "IOC",
    "post_only": false,
    "builder": {
      "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
      "fee_bps": 25
    },
    "negrisk_aware": false,
    "decision": {
      "edge_bps": 400.0,
      "fair_value": 1.0,
      "clob_mid": 0.96,
      "reasons": [
        "RFV_EDGE_TRADE"
      ]
    },
    "comment": "fees are operator-set at match time in V2 \u2014 feeRateBps is NOT on the signed order"
  },
  "developer_log": {
    "bot_id": "strat.resolution_fair_value",
    "market_id": "0xrfv0000000000000000000000000000000000000000000000000000000000001",
    "fair_value": 1.0,
    "clob_mid": 0.96,
    "edge_bps": 400.0,
    "oracle_fresh": true,
    "dispute_open": false,
    "intent_emitted": true,
    "reason": "RFV_EDGE_TRADE",
    "emitted_at_ms": 1746790800000
  },
  "user_explanations": [
    {
      "situation": "Near-resolution trade placed",
      "message": "The market was priced below what the authoritative data suggests. An order was placed to capture the mispricing."
    },
    {
      "situation": "Oracle not clean \u2014 no trade",
      "message": "The data source was stale or a dispute was open. No trade was placed until the oracle is confirmed."
    },
    {
      "situation": "Ambiguous resolution rule",
      "message": "The market's resolution criteria were not fully clear. No trade was placed."
    }
  ],
  "failure_modes": {
    "main_failure_mode": "Ambiguous resolution: market resolves N/A or in an unexpected direction because the resolution rule was less clear than the oracle signal suggested.",
    "false_positive_risk": "Oracle signal appears clean but is based on preliminary data that gets revised before official resolution.",
    "false_negative_risk": "min_edge_bps set too high misses genuine near-certainty mispricings on liquid markets.",
    "safe_fallback": "If oracle stale or dispute open, emit RFV_ORACLE_NOT_CLEAN and skip without emitting any OrderIntent.",
    "required_dependencies": [
      "ws_market",
      "clob_public",
      "onchain UMA dispute status",
      "internal resolution tracker",
      "KillSwitch",
      "internal builder code"
    ]
  },
  "acceptance_tests": {
    "unit": [
      {
        "test": "Emit IOC when edge=400 bps, oracle clean, no dispute",
        "setup": "min_edge_bps=100, fair_value=1.0, clob_mid=0.960",
        "expected": "IOC OrderIntent; reason=RFV_EDGE_TRADE"
      },
      {
        "test": "Skip when dispute open",
        "setup": "uma_dispute=open",
        "expected": "No OrderIntent; reason=RFV_ORACLE_NOT_CLEAN"
      },
      {
        "test": "Skip when edge < 20 bps hard floor",
        "setup": "fair_value=0.980, clob_mid=0.979",
        "expected": "No OrderIntent; sampled reason=RFV_NO_EDGE"
      }
    ],
    "integration": [
      {
        "test": "Full cycle: oracle signal \u2192 fair value \u2192 IOC OrderIntent on Polygon testnet",
        "expected": "Order has builder.code, no feeRateBps, EIP-712 domain v2"
      }
    ],
    "property": [
      {
        "property": "Bot never trades when dispute is open",
        "required": "Always true"
      },
      {
        "property": "feeRateBps never present on any signed OrderIntent",
        "required": "Always true"
      }
    ]
  },
  "checklist_overrides": {},
  "legacy_goal": "Trade pre-resolution mispricings \u2014 markets that should be \u00a299 but trade at \u00a296.",
  "legacy_pm_signals": [
    "Market price vs. fee-adjusted intrinsic value",
    "OracleRiskMonitor \"no active dispute\" approval",
    "Resolution source clarity flag"
  ],
  "legacy_external_feeds": [
    "NewsIngest + structured event sources confirm event has occurred"
  ],
  "reporting_groups": [
    "strategy_decision"
  ],
  "reason_codes": [
    {
      "code": "RFV_EDGE_TRADE",
      "severity": "INFO",
      "meaning": "edge_bps >= min_edge_bps, oracle clean, no dispute. IOC OrderIntent emitted.",
      "action": "Emit IOC OrderIntent.",
      "user_message": "A mispricing near resolution was found and a trade was placed."
    },
    {
      "code": "RFV_NO_EDGE",
      "severity": "INFO",
      "meaning": "edge_bps < 20 bps hard floor. Price is sufficiently close to fair value.",
      "action": "Skip; emit sampled DecisionReport.",
      "user_message": "The market was already priced close to fair value."
    },
    {
      "code": "RFV_EDGE_MARGINAL",
      "severity": "WARN",
      "meaning": "edge_bps between 20 and 50 bps; size reduced 50%.",
      "action": "Emit at 50% size; log warning.",
      "user_message": "A small mispricing was found; a reduced-size trade was placed."
    },
    {
      "code": "RFV_ORACLE_NOT_CLEAN",
      "severity": "HARD_REJECT",
      "meaning": "Oracle data is stale or a UMA dispute is open.",
      "action": "Skip; no OrderIntent.",
      "user_message": "Data or dispute status blocked the trade."
    },
    {
      "code": "RFV_AMBIGUOUS_SOURCE",
      "severity": "HARD_REJECT",
      "meaning": "Resolution rule parsing confidence is not at maximum.",
      "action": "Skip; no OrderIntent.",
      "user_message": "Resolution rules were not fully clear \u2014 no trade placed."
    }
  ],
  "metrics": {
    "emitted": [
      {
        "name": "polytraders_strat_rfv_decisions_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "verdict",
          "reason_code"
        ],
        "meaning": "Total evaluation cycles by verdict and reason code."
      },
      {
        "name": "polytraders_strat_rfv_edge_bps",
        "type": "histogram",
        "unit": "basis_points",
        "labels": [],
        "meaning": "Distribution of fair-value edge in bps."
      },
      {
        "name": "polytraders_strat_rfv_intents_emitted_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "outcome"
        ],
        "meaning": "Total IOC OrderIntents emitted by outcome."
      },
      {
        "name": "polytraders_strat_rfv_eval_latency_ms",
        "type": "histogram",
        "unit": "milliseconds",
        "labels": [],
        "meaning": "Latency from oracle signal to OrderIntent emit."
      }
    ],
    "alerts": [
      {
        "name": "RFVOracleNotClean",
        "condition": "rate(polytraders_strat_rfv_decisions_total{reason_code='RFV_ORACLE_NOT_CLEAN'}[5m]) > 0.2",
        "severity": "warn",
        "runbook": "#runbook-rfv-oracle"
      },
      {
        "name": "RFVKillSwitch",
        "condition": "rate(polytraders_strat_rfv_decisions_total{reason_code='KILL_SWITCH_ACTIVE'}[1m]) > 0",
        "severity": "page",
        "runbook": "#runbook-killswitch"
      }
    ]
  },
  "state": {
    "store": "redis",
    "shape": "Per market: last oracle signal, fair_value estimate, dispute_open flag; keyed by market_id",
    "ttl": "60s per oracle snapshot; dispute flag refreshed every 30s",
    "recovery": "On cold start, oracle signals rebuilt from next resolution-tracker poll; dispute state re-fetched from onchain.",
    "size_estimate": "~200 bytes per tracked market; < 1 MB total"
  },
  "concurrency": {
    "execution_model": "actor-per-market",
    "max_in_flight": 30,
    "idempotency_key": "intent_id",
    "timeout_ms": 250,
    "backpressure": "drop oldest oracle update per market_id when queue depth > 2",
    "locking": "per-market_id mutex for oracle state"
  },
  "dependencies": {
    "depends_on": [
      {
        "bot_id": "risk.kill_switch",
        "why": "Checked first; blocks all intent emission when active."
      }
    ],
    "emits_to": [
      {
        "bot_id": "risk.portfolio_guard",
        "what": "IOC OrderIntents for risk guardrail evaluation."
      },
      {
        "bot_id": "gov.builder_attribution",
        "what": "builder.code bytes32 on every OrderIntent."
      }
    ],
    "sibling": [],
    "external": [
      {
        "service": "Polymarket CLOB WebSocket (ws_market)",
        "sla": "best-effort",
        "fallback": "Halt on disconnect."
      },
      {
        "service": "UMA Optimistic Oracle (onchain)",
        "sla": "99.9%",
        "fallback": "If dispute status fetch fails, treat as dispute_open=true and skip."
      }
    ]
  },
  "security_surfaces": {
    "signs_orders": true,
    "private_key_access": "signing-only",
    "abuse_vectors": [
      "Oracle signal spoofing to induce trades on incorrect fair values",
      "Dispute-status delay causing bot to trade during open dispute window"
    ],
    "mitigations": [
      "Oracle signals sourced from internal resolution tracker using authenticated feeds",
      "UMA dispute status fetched directly from onchain; staleness treated as dispute_open=true"
    ]
  },
  "failure_injection": [
    {
      "scenario": "ORACLE_STALE",
      "how_to_inject": "Freeze resolution tracker updates for > 60s",
      "expected_behaviour": "RFV_ORACLE_NOT_CLEAN; no OrderIntents",
      "recovery": "Automatic when tracker resumes."
    },
    {
      "scenario": "DISPUTE_OPEN",
      "how_to_inject": "Set uma_dispute=open for test market",
      "expected_behaviour": "RFV_ORACLE_NOT_CLEAN; no OrderIntent for that market",
      "recovery": "Automatic when dispute window closes."
    },
    {
      "scenario": "KILL_SWITCH_ON",
      "how_to_inject": "Set killswitch.active=true",
      "expected_behaviour": "No OrderIntents emitted",
      "recovery": "Automatic on manual KillSwitch reset."
    }
  ],
  "runbook": {
    "summary": "RFV incidents are typically oracle staleness or open UMA disputes blocking trades. Escalate if oracle staleness persists > 5 min on active markets near resolution.",
    "oncall_actions": [
      {
        "alert": "RFVOracleNotClean",
        "first_action": "Check resolution tracker health and UMA dispute log for affected markets.",
        "escalate_to": "Infra on-call if resolution tracker down > 5 min."
      },
      {
        "alert": "RFVKillSwitch",
        "first_action": "Confirm KillSwitch activation was intentional.",
        "escalate_to": "Risk pod lead immediately."
      }
    ],
    "manual_overrides": [
      {
        "name": "exclude_market",
        "how": "Add market_id to config.excluded_markets",
        "when": "Market has an unexpected ambiguity or is under active dispute review."
      }
    ],
    "healthcheck": "GET /internal/health/resolution-fair-value -> 200 if Oracle snapshots < 60s old; no markets blocked by open disputes for > 30s; KillSwitch inactive.. Red: Resolution tracker down or KillSwitch active.."
  },
  "promotion_gates": {
    "to_shadow": [
      {
        "gate": "Unit tests pass including dispute-open block and oracle-stale block",
        "how_measured": "CI test run",
        "threshold": "100% pass"
      }
    ],
    "to_limited_live": [
      {
        "gate": "p99 eval latency < 250ms over 24h shadow run",
        "how_measured": "polytraders_strat_rfv_eval_latency_ms histogram",
        "threshold": "p99 < 250ms"
      }
    ],
    "to_general_live": [
      {
        "gate": "E2E: oracle signal \u2192 fair value \u2192 IOC OrderIntent on Polygon testnet with no feeRateBps",
        "how_measured": "E2E test",
        "threshold": "Pass"
      }
    ]
  },
  "wire_examples": {
    "input": [
      {
        "label": "Oracle signal \u2014 market at \u00a296 with fair value 1.0",
        "source": "internal (resolution tracker)",
        "payload": {
          "market_id": "0xrfv0000000000000000000000000000000000000000000000000000000000001",
          "fair_value": "1.0",
          "clob_mid": "0.960",
          "oracle_fresh": true,
          "dispute_open": false,
          "received_at_ms": 1746790800000
        }
      }
    ],
    "output": [
      {
        "label": "OrderIntent \u2014 RFV IOC buy YES",
        "payload": {
          "intent_id": "oi_01HRFV0000001A",
          "market_id": "0xrfv0000000000000000000000000000000000000000000000000000000000001",
          "outcome": "YES",
          "side": "buy",
          "price": "0.960",
          "size_pUSD": "300.00",
          "tif": "IOC",
          "builder": {
            "code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
            "fee_bps": 25
          },
          "decision": {
            "edge_bps": 400.0,
            "reasons": [
              "RFV_EDGE_TRADE"
            ]
          }
        }
      }
    ]
  },
  "reference_implementation": {
    "pseudocode": "FUNCTION onOracleUpdate(market_id, oracleSignal):\n  ks = FETCH internal.killswitch.status\n  IF ks.active: RETURN\n\n  mkt = FETCH clob_public.GET('/markets/' + market_id)\n  IF mkt.closed OR mkt.resolved: RETURN\n\n  // Oracle freshness and dispute checks\n  IF oracleSignal.stale OR NOT oracleSignal.source_unambiguous:\n    EMIT DecisionReport(intent_emitted=false, reason='RFV_ORACLE_NOT_CLEAN')\n    RETURN\n\n  disputeStatus = FETCH onchain.UMADisputeStatus(market_id)\n  IF disputeStatus.open:\n    EMIT DecisionReport(intent_emitted=false, reason='RFV_ORACLE_NOT_CLEAN')\n    RETURN\n\n  // Edge computation\n  book = FETCH ws_market.book(market_id)\n  clobMid = (book.best_bid + book.best_ask) / 2\n  fairValue = oracleSignal.fair_value\n  edgeBps = abs(fairValue - clobMid) * 10000\n\n  IF edgeBps < params.min_edge_bps_hard:  // 20 bps\n    IF random() < 0.01:\n      EMIT DecisionReport(intent_emitted=false, reason='RFV_NO_EDGE')\n    RETURN\n\n  sizeMultiplier = 0.5 IF edgeBps < params.min_edge_bps_warn ELSE 1.0\n  IF sizeMultiplier < 1.0: WARN('RFV_EDGE_MARGINAL')\n\n  depth = FETCH clob_public.depth(market_id)\n  orderSize = toPusdUnits(min(params.max_size_per_market_usd * sizeMultiplier, depth.available))\n  direction = 'YES' IF fairValue > clobMid ELSE 'NO'\n\n  EMIT OrderIntent(market=market_id, outcome=direction, side='buy', price=clobMid,\n                   size_pUSD=orderSize, tif='IOC', builder=internalBuilderCode)\n  EMIT DecisionReport(intent_emitted=true, edge_bps=edgeBps, reason='RFV_EDGE_TRADE')",
    "sdk_calls": [
      "ws_market.subscribe('book', [market_id])",
      "fetchClobPublic('/markets/' + market_id)",
      "onchain.UMADisputeStatus(market_id)",
      "internal.resolutionTracker.signal(market_id)",
      "buildOrderTypedData(orderParams, {name:'CTFExchange', version:'2', chainId:137})"
    ],
    "complexity": "O(1) per oracle signal per market"
  },
  "api_surface": [
    "clob_public",
    "clob_auth",
    "ws_market",
    "onchain",
    "internal"
  ],
  "network": [
    "polygon"
  ],
  "version": {
    "spec": "2.0.0",
    "implementation": "0.1.0",
    "schema": "2",
    "released": null,
    "planned_release": "Q3-2026"
  },
  "migration_history": [
    {
      "date": "2026-04-28",
      "from": "n/a",
      "to": "v2-spec",
      "reason": "Spec drafted post-CLOB-V2 cutover; bot not yet implemented",
      "action_taken": "Designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain)"
    }
  ],
  "polymarket_v2_compat": {
    "clob_version": "v2",
    "collateral": "pUSD",
    "eip712_domain_version": "2",
    "builder_code_aware": true,
    "negrisk_aware": false,
    "multichain_ready": false,
    "sdk_used": "py-clob-client-v2",
    "settlement_contract": "CTFExchangeV2",
    "notes": "Bot not yet implemented; designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain). feeRateBps not present on any signed OrderIntent."
  },
  "reporting": {
    "emits_kinds": [
      "DecisionReport"
    ],
    "topics": [
      "polytraders.reports.decision"
    ],
    "cadence": "every-event",
    "retention_class": "2y",
    "sampling_rule": "emit-every",
    "bus_failure_action": "fail-closed",
    "user_visible": "yes",
    "consumes_kinds": [
      "ObservationReport",
      "RiskVote"
    ]
  },
  "capital_impact": "Direct",
  "v3_status": {
    "phase": 8,
    "phase_name": "Additional strategies",
    "docs": {
      "done": 27,
      "total": 27,
      "state": "done"
    },
    "impl": {
      "done": 0,
      "total": 15,
      "state": "pending"
    },
    "runtime": {
      "done": 0,
      "total": 8,
      "state": "pending"
    },
    "overall": "pending"
  }
}