oak_techniques: [OAK-T9.006.002]
spec_id: oak-detection-T9.006.002
version: 0.1.0
maturity: emerging
maintainer: "@iZonex"
license: Apache-2.0

scope: |
  Detect prediction-market resolutions where the natural-language spec
  is interpretively underdetermined and voters resolve against bettor-
  majority interpretation citing spec ambiguity. The detection surface
  is at the spec-design layer: any market with a non-machine-checkable
  resolution predicate is exposed. Excludes: T9.006.001 (vote-capture
  against the criteria — voters in T9.006.002 vote a reasonable
  interpretation); T9.006.003 (resolution-input attack — the input is
  honest, the spec is ambiguous); markets with cryptographically-
  verifiable resolution criteria (block-height events, on-chain state,
  signed attestations).

data_sources: [market_resolution_spec, oracle_vote_logs,
               oracle_vote_rationale, off_chain_news_corpus,
               historical_dispute_data]

detection_logic:
  description: |
    Three orthogonal paths. PATH A (pre-listing static): spec text
    contains unmachine-checkable predicates from a controlled
    ambiguity-vocabulary, or names < min_named_sources canonical
    sources. PATH B (vote-rationale): voter justifications cite spec
    ambiguity language above a cohort threshold. PATH C (media-vs-
    oracle divergence): media-consensus signal contradicts oracle
    outcome and post-resolution disputed-mention volume on the
    market spikes.
  pseudocode: |
    AMBIGUITY_VOCAB = {
      wardrobe:    [suit, formal_attire, dress, uniform],
      political:   [agreed, deal, official, recognized],
      interpretive: [credible, consensus, genuine, verified],
      hedge_words: [substantially, materially, reasonably],
    }
    AMBIGUITY_RATIONALE = [
      "lack of credible reporting consensus",
      "no clear definition", "subjective interpretation",
      "spec ambiguity", "open to interpretation",
    ]

    # PATH A — pre-listing spec analysis
    for each market M at listing time:
      tokens ← tokenize(M.resolution_spec)
      ambiguous_terms ← tokens ∩ flatten(AMBIGUITY_VOCAB)
      named_sources ← extract_named_sources(M.resolution_spec)
      if ambiguous_terms ≠ ∅ or |named_sources| < min_named_sources:
        emit(PATH_A, market=M, ambiguous_terms, named_sources,
             severity=high)

    # PATH B — vote-rationale ambiguity citation
    for each resolution round R on market M:
      rationales ← collect_voter_rationales(R)
      hits ← [r for r in rationales
              if any(p in r.text.lower() for p in AMBIGUITY_RATIONALE)]
      share ← Σ r.weight for r in hits / total_weight(R)
      if share > rationale_share_threshold:
        emit(PATH_B, market=M, ambiguity_share=share,
             sample_rationales=top_k_by_weight(hits, k=5),
             severity=critical)

    # PATH C — media-vs-oracle divergence + dispute volume
    for each resolved market M with adjudicated_volume_usd(M) > min_market_volume_usd:
      consensus ← media_consensus(M.claim, sources = consensus_sources,
                                   window = pre_resolution_window)
      dispute  ← post_resolution_mention_volume(M, window = post_window)
      if consensus ≠ None and disagreement(M.outcome, consensus) > divergence_threshold
         and dispute > dispute_volume_threshold:
        emit(PATH_C, market=M, divergence=consensus, dispute_volume=dispute,
             severity=high)

parameters:
  min_named_sources:          { type: integer, default: 2 }
  rationale_share_threshold:  { type: number,  default: 0.3 }
  min_market_volume_usd:      { type: number,  default: 1000000 }
  consensus_sources:          { type: list,    default: [reuters, ap, afp, bbc] }
  pre_resolution_window:      { type: duration, default: 7d }
  divergence_threshold:       { type: number,  default: 0.7 }
  post_window:                { type: duration, default: 7d }
  dispute_volume_threshold:   { type: integer, default: 500 }

output_alert: [oak_technique, detection_path, severity, market,
               ambiguous_terms, ambiguity_share, divergence, evidence]

test_fixtures:
  positive:
    - 2025-07-polymarket-zelenskyy-suit                 # canonical: "wearing a suit" + "credible reporting consensus"
  negative:
    - "Polymarket block-height-resolved market (machine-checkable predicate)"
    - "Polymarket sports market with single-source canonical scoreboard"

false_positive_modes:
  - markets whose resolution criteria use ambiguity vocabulary in a non-load-bearing way (e.g., descriptive prose around a machine-checkable predicate) — extract the load-bearing predicate
  - markets where rationale share crosses threshold because of a single high-weight delegate's idiosyncratic phrasing — require multiple voters
  - genuinely contested real-world facts where multiple media outlets actually disagreed at resolution time — divergence threshold should reflect cohort, not individual-source disagreement
  - linguistic edge-cases (translations, colloquialisms) that trigger AMBIGUITY_VOCAB without spec-design intent — calibrate via per-locale stoplists

mitigations: [OAK-M02, OAK-M16]

reference_implementations:
  - { target: dune,                chain: polygon, url: "" }
  - { target: chainalysis-polymarket-surveillance, chain: polygon, url: "" }
  - { target: market-spec-linter,  chain: polygon, url: "" }
