{
  "schema_version": "1.0.0",
  "bot_id": "2.15",
  "bot_name": "OrderAmendCancelManager",
  "slug": "order_amend_cancel_manager",
  "layer": "Execution",
  "layer_key": "exec",
  "bot_class": "Execution Utility",
  "authority": [
    "Amend",
    "Cancel"
  ],
  "status": "planned",
  "readiness": "Spec ready",
  "flagship": false,
  "is_reference": false,
  "public_export": false,
  "identity": {
    "layer": "Execution",
    "bot_class": "ExecutionUtility",
    "authority": "Amend, Cancel",
    "runs_before": "\u2014",
    "runs_after": "exec.order_lifecycle_manager",
    "applies_to": "Continuous",
    "default_mode": "shadow",
    "user_visible": "Yes",
    "developer_owner": "Execution pod"
  },
  "purpose": "Owns every amend and cancel operation against the CLOB for orders Polytraders has placed. Strategies do not cancel directly \u2014 they emit an OrderAdjustment intent which this bot validates, sequences, and submits via the EIP-712 v2 envelope. Maintains a strict monotonic intent sequence per order so cancels can never race amends.",
  "why_it_matters": [
    {
      "failure": "Race conditions on cancel/amend",
      "consequence": "Without a single sequencer, two strategies can race each other to amend the same order, producing inconsistent CLOB state."
    },
    {
      "failure": "Phantom cancels",
      "consequence": "A cancel that fails silently leaves an order resting that strategy code believes has been killed; mismatched state causes self-trades and over-positioning."
    },
    {
      "failure": "Amend bypasses risk",
      "consequence": "An 'amend size from 100 to 500' looks like a cancel-and-place but bypasses Risk if not routed through this bot."
    }
  ],
  "polymarket_inputs": [
    {
      "input": "CLOB v2 amend/cancel endpoints",
      "source": "CLOB",
      "required": true,
      "use": "The actual targets for the action."
    },
    {
      "input": "Order ack/reject events",
      "source": "WebSocket",
      "required": true,
      "use": "Confirm or fail each operation."
    }
  ],
  "internal_inputs": [
    {
      "input": "OrderAdjustment intent",
      "source": "Strategy or runbook",
      "required": true,
      "use": "The request to modify or cancel an order."
    },
    {
      "input": "OrderLifecycleManager state",
      "source": "exec.order_lifecycle_manager",
      "required": true,
      "use": "Source of truth for order status before and after."
    },
    {
      "input": "RiskVote pipeline",
      "source": "Risk pod",
      "required": true,
      "use": "Amends with non-zero new_size must pass Risk like any other OrderIntent."
    }
  ],
  "raw_params": [
    "amend_timeout_ms \u00b7 100\u201310000",
    "cancel_timeout_ms \u00b7 100\u201310000",
    "max_inflight_per_order \u00b7 1\u20135"
  ],
  "parameters": [
    {
      "name": "amend_timeout_ms",
      "default": 1500,
      "warning": "1500ms",
      "hard": "3000ms",
      "controls": "Maximum wait for an amend ack before retrying or escalating.",
      "why_default_matters": "1.5s covers normal CLOB latency; longer timeouts hide real outages.",
      "threshold_logic": [
        {
          "condition": "\u2264 1500ms",
          "action": "Normal"
        },
        {
          "condition": "> 1500ms",
          "action": "TIMEOUT \u2014 retry once, then escalate"
        }
      ],
      "dev_check": "if (now - sent > p.amend_timeout_ms) retry_or_escalate();",
      "user_facing": "We could not change this order in time and rolled it back."
    },
    {
      "name": "cancel_timeout_ms",
      "default": 1500,
      "warning": "\u2014",
      "hard": "\u2014",
      "controls": "Maximum wait for a cancel ack before retrying or escalating.",
      "why_default_matters": "Same reasoning as amend timeout.",
      "threshold_logic": [
        {
          "condition": "\u2264 1500ms",
          "action": "Normal"
        },
        {
          "condition": "> 1500ms",
          "action": "TIMEOUT \u2014 retry once, then escalate"
        }
      ],
      "dev_check": "if (now - sent > p.cancel_timeout_ms) retry_or_escalate();",
      "user_facing": "We could not cancel this order in time."
    },
    {
      "name": "max_inflight_per_order",
      "default": 1,
      "warning": "1",
      "hard": "2",
      "controls": "Maximum concurrent amend/cancel operations per single order_id.",
      "why_default_matters": "1 enforces strict serialisation per order \u2014 required to keep state consistent.",
      "threshold_logic": [
        {
          "condition": "1",
          "action": "Default \u2014 strict serialisation"
        }
      ],
      "dev_check": "if (inflight[order_id] >= p.max_inflight_per_order) reject_intent('EXEC_AMEND_INFLIGHT');",
      "user_facing": "(Internal.)"
    }
  ],
  "default_config": {
    "amend_timeout_ms": 1500,
    "cancel_timeout_ms": 1500,
    "max_inflight_per_order": 1
  },
  "flow": "Receive OrderAdjustment(order_id, op=amend|cancel, new_size?, new_price?) \u2192 validate against OrderLifecycleManager \u2192 if amend with new_size > current_size: route through Risk pipeline as a fresh OrderIntent for the delta \u2192 assign monotonic seq_id per order_id \u2192 submit to CLOB v2 with EIP-712 v2 envelope \u2192 await ack within timeout \u2192 on success update OrderLifecycleManager \u2192 on timeout retry once, then escalate via OperationsReport.",
  "decision_logic": {
    "approve": "Sequence operations strictly per order_id.",
    "reshape_required": "This bot does not reshape orders.",
    "reject": "Reject if max_inflight_per_order is exceeded. Reject amends that would increase size without Risk approval. Surface every ack/timeout/reject as a structured ReportEnvelope.",
    "warning_only": "No warn-only path defined."
  },
  "decision_output_example": {
    "intent_id": "adj_001",
    "order_id": "ord_xyz",
    "op": "amend",
    "new_size_usd": 50,
    "outcome": "ACK",
    "seq_id": 7,
    "reason_code": "EXEC_AMEND_OK"
  },
  "developer_log": "Per operation: order_id, op, seq_id, sent_ts_ms, acked_ts_ms, outcome, reason_code.",
  "user_explanations": [
    {
      "situation": "When this bot acts",
      "message": "This change to your order was applied successfully."
    },
    {
      "situation": "When this bot acts",
      "message": "We could not change this order in time and rolled it back."
    }
  ],
  "failure_modes": {
    "main_failure_mode": "An ack arriving after the timeout window: bot has already retried; CLOB has two operations applied.",
    "false_positive_risk": "Reporting cancel-failure when the cancel actually succeeded; mitigation: reconcile against OrderLifecycleManager before retrying.",
    "false_negative_risk": "Strategy code bypassing the bot via direct CLOB calls; mitigation: outbound CLOB writes are restricted at the network layer to this bot's identity only.",
    "safe_fallback": "On unrecoverable inconsistency, mark the order as QUARANTINED and escalate to the Execution on-call. Never silently retry past max_retries."
  },
  "acceptance_tests": {
    "unit": [
      {
        "test": "Cancel of an unknown order_id returns EXEC_AMEND_UNKNOWN.",
        "setup": "Synthetic fixture per template.",
        "expected": "Behaviour matches the rule described in the test name."
      },
      {
        "test": "Concurrent amend on the same order_id returns EXEC_AMEND_INFLIGHT for the second one.",
        "setup": "Synthetic fixture per template.",
        "expected": "Behaviour matches the rule described in the test name."
      }
    ],
    "integration": [
      {
        "test": "Replay 1000 amend/cancel ops through a fixture CLOB; assert every op produces exactly one ReportEnvelope.",
        "expected": "End-to-end behaviour matches the spec without manual intervention."
      }
    ],
    "property": [
      {
        "property": "seq_id is strictly increasing per (order_id).",
        "required": "Always true across all generated inputs."
      }
    ]
  },
  "reference_implementation": {
    "language": "pseudocode",
    "pseudocode": "validate(adjustment)\nif op == 'amend' and new_size > current.size:\n  pass_through_risk(delta_intent)\nseq = next_seq(order_id)\nresult = clob.send(adjustment, seq, eip712_v2_envelope())\nwait_ack_or_timeout(p.amend_timeout_ms)\nreconcile_with_lifecycle()\nemit_report()"
  },
  "wire_examples": {
    "input": {
      "intent_id": "adj_001",
      "order_id": "ord_xyz",
      "op": "amend",
      "new_size_usd": 50
    },
    "output": {
      "intent_id": "adj_001",
      "order_id": "ord_xyz",
      "outcome": "ACK",
      "seq_id": 7,
      "reason_code": "EXEC_AMEND_OK"
    }
  },
  "reason_codes": [
    {
      "code": "EXEC_AMEND_OK",
      "severity": "P2",
      "meaning": "Exec Amend Ok",
      "action": "See decision output and developer log for context.",
      "user_message": "This change to your order was applied successfully."
    },
    {
      "code": "EXEC_CANCEL_OK",
      "severity": "P2",
      "meaning": "Exec Cancel Ok",
      "action": "See decision output and developer log for context.",
      "user_message": "This change to your order was applied successfully."
    },
    {
      "code": "EXEC_AMEND_INFLIGHT",
      "severity": "P2",
      "meaning": "Exec Amend Inflight",
      "action": "See decision output and developer log for context.",
      "user_message": "This change to your order was applied successfully."
    },
    {
      "code": "EXEC_AMEND_UNKNOWN",
      "severity": "P2",
      "meaning": "Exec Amend Unknown",
      "action": "See decision output and developer log for context.",
      "user_message": "This change to your order was applied successfully."
    },
    {
      "code": "EXEC_AMEND_TIMEOUT",
      "severity": "P2",
      "meaning": "Exec Amend Timeout",
      "action": "See decision output and developer log for context.",
      "user_message": "This change to your order was applied successfully."
    },
    {
      "code": "EXEC_CANCEL_TIMEOUT",
      "severity": "P2",
      "meaning": "Exec Cancel Timeout",
      "action": "See decision output and developer log for context.",
      "user_message": "This change to your order was applied successfully."
    }
  ],
  "metrics": {
    "emitted": [
      {
        "name": "amend_ops_total",
        "type": "counter",
        "unit": "event",
        "labels": [
          "bot_id"
        ],
        "meaning": "Amend ops total."
      },
      {
        "name": "cancel_ops_total",
        "type": "counter",
        "unit": "event",
        "labels": [
          "bot_id"
        ],
        "meaning": "Cancel ops total."
      },
      {
        "name": "amend_timeout_total",
        "type": "counter",
        "unit": "event",
        "labels": [
          "bot_id"
        ],
        "meaning": "Amend timeout total."
      },
      {
        "name": "cancel_timeout_total",
        "type": "counter",
        "unit": "event",
        "labels": [
          "bot_id"
        ],
        "meaning": "Cancel timeout total."
      },
      {
        "name": "ack_latency_ms_histogram",
        "type": "counter",
        "unit": "event",
        "labels": [
          "bot_id"
        ],
        "meaning": "Ack latency ms histogram."
      }
    ],
    "alerts": [],
    "dashboards": [
      "2.15 overview dashboard"
    ]
  },
  "state": {
    "summary": "Per-order seq_id counter (durable). In-flight operation map (in-memory).",
    "stores": [
      {
        "name": "order_amend_cancel_manager_state",
        "kind": "in-memory + fast KV mirror",
        "key": "bot_id",
        "value": "Per-order seq_id counter (durable). In-flight operation map (in-memory).",
        "ttl": "24h",
        "durability": "crash-safe via KV mirror"
      }
    ],
    "recovery": "Cold-start hydrates from fast KV; missing keys default to safe fallback.",
    "on_restart": "All in-flight decisions are re-evaluated; no bot decision is trusted across restart without re-emit."
  },
  "concurrency": {
    "execution_model": "Strict per-order serialisation. Concurrent across distinct order_ids.",
    "max_in_flight": 32,
    "idempotency_key": "order_intent_id",
    "replay_safe": true,
    "deduplication": "By idempotency_key within a 60s window.",
    "ordering_guarantees": "Per-market_id FIFO; cross-market unordered.",
    "timeout_ms": 250,
    "backpressure": "Bounded queue; oldest-dropped with metric increment when full.",
    "locking": "Per-market_id mutex; no global locks."
  },
  "dependencies": {
    "depends_on": [
      "exec.order_lifecycle_manager"
    ],
    "emits_to": []
  },
  "graph": {
    "requires": [
      "exec.order_lifecycle_manager"
    ],
    "required_before": [],
    "consumes": [
      "OrderAdjustment",
      "OrderLifecycleState"
    ],
    "emits": [
      "ReportEnvelope(kind=ExecutionReport)"
    ],
    "blocks": false
  },
  "mode_support": [
    "off",
    "shadow",
    "advisory",
    "enforced",
    "quarantine"
  ],
  "latency_budget_ms": {
    "p50": 25,
    "p99": 250
  },
  "data_freshness": {
    "max_market_data_age_ms": 5000,
    "max_orderbook_age_ms": 5000,
    "on_stale_data": "Reject the adjustment with EXEC_AMEND_STALE_VIEW."
  },
  "ownership": {
    "owner": "Execution pod",
    "on_call": "exec-oncall",
    "channel": "#polytraders-exec",
    "escalation": "Head of Execution",
    "severity_class": "P1"
  },
  "human_override": {
    "allowed": true,
    "who": "Execution on-call",
    "log_event": "EXEC_AMEND_OVERRIDE",
    "time_bound": "Per incident",
    "scope": "Single order_id",
    "second_approval": true
  },
  "security_surfaces": {
    "summary": "Only this bot's identity has CLOB write permission for amend/cancel.",
    "signing": "Reads pending signed orders; never signs on user behalf.",
    "secrets": [],
    "contract_calls": [],
    "abuse_vectors": [
      "EIP-712 v2 signature required for every operation."
    ],
    "mitigations": [
      "Rate-limit per source",
      "Audit-log every override",
      "Require role-based authz on admin paths"
    ]
  },
  "polymarket_v2_compat": {
    "clob_version": "V2",
    "collateral": "pUSD",
    "eip712_domain_version": "2",
    "builder_code_aware": true,
    "negrisk_aware": true,
    "multichain_ready": true,
    "sdk_used": "Polymarket CLOB V2 SDK",
    "settlement_contract": "CTFExchangeV2",
    "notes": "Native CLOB v2 amend/cancel endpoints; EIP-712 v2 envelope with builderCode."
  },
  "version": {
    "current": "0.1.0",
    "contract_version": "1.0.0",
    "last_breaking_change": "none",
    "deprecation_window_days": 30
  },
  "migration_history": [],
  "runbook": {
    "summary": "If timeouts spike: inspect CLOB v2 latency; enable verbose ack-trace logging; consider widening timeouts only as a temporary measure.",
    "oncall_actions": [
      {
        "alert": "2.15_anomaly",
        "first_step": "Open the bot's reporting page and confirm the alert is real (not a metric hiccup).",
        "diagnosis": "Inspect developer log entries for the affected market_id over the last 30 minutes.",
        "mitigation": "Force-clear via Admin UI if the rule is clearly stale; otherwise leave engaged and notify owner.",
        "escalation": "Execution pod"
      }
    ],
    "manual_overrides": [
      {
        "command": "polytraders bot pause 2.15",
        "effect": "Disables the bot's enforcement layer; downstream consumers fall back to safe defaults."
      }
    ],
    "healthcheck": "GET /healthz/order_amend_cancel_manager \u2192 200 if last successful evaluation < 60s ago."
  },
  "promotion_gates": {
    "to_shadow": [
      {
        "gate": "Stub",
        "how_measured": "against fixture CLOB.",
        "threshold": "Documented threshold met for the full window."
      }
    ],
    "to_limited_live": [
      {
        "gate": "Shadow",
        "how_measured": "14 days mirrored against live state.",
        "threshold": "Documented threshold met for the full window."
      },
      {
        "gate": "Advisory",
        "how_measured": "7 days.",
        "threshold": "Documented threshold met for the full window."
      }
    ],
    "to_general_live": [
      {
        "gate": "Enforced",
        "how_measured": "every cancel/amend in the system routes through this bot.",
        "threshold": "Documented threshold met for the full window."
      }
    ]
  },
  "failure_injection": [
    {
      "scenario": "Drop ack from CLOB and assert single retry then escalation",
      "how_to_inject": "Drop ack from CLOB and assert single retry then escalation.",
      "expected_behavior": "Bot detects within its latency budget and emits the corresponding reason code.",
      "recovery": "Remove the injected fault; bot returns to healthy state within one debounce window."
    },
    {
      "scenario": "Race two amends on the same order and assert second is rejected with INFLIGHT",
      "how_to_inject": "Race two amends on the same order and assert second is rejected with INFLIGHT.",
      "expected_behavior": "Bot detects within its latency budget and emits the corresponding reason code.",
      "recovery": "Remove the injected fault; bot returns to healthy state within one debounce window."
    }
  ],
  "capital_impact": "Direct",
  "v3_status": {
    "phase": 5,
    "phase_name": "Execution rails",
    "docs": {
      "done": 27,
      "total": 27,
      "state": "done"
    },
    "impl": {
      "done": 0,
      "total": 15,
      "state": "pending"
    },
    "runtime": {
      "done": 0,
      "total": 8,
      "state": "pending"
    },
    "overall": "pending"
  }
}