{
  "format": "siebly-integration-kit/v1",
  "id": "exchange-state-management",
  "title": "Exchange State Management Integration Kit",
  "lastReviewed": "2026-05-12",
  "purpose": "Exchange-neutral runtime contract for order, account, and position state-management systems. Exchange-specific kits add SDK methods, request fields, topics, and error codes.",
  "specGuide": "https://siebly.io/ai/exchange-state",
  "agentImplementationManifest": "https://siebly.io/.well-known/agent-manifests/exchange-state-management/latest.json",
  "runtimeKit": "https://siebly.io/.well-known/runtime-kits/exchange-state-management/v1/index.json",
  "conformancePack": "https://siebly.io/.well-known/conformance/exchange-state-management/latest.json",
  "primarySpec": {
    "id": "exchange-state-management-spec",
    "title": "Exchange State Management",
    "href": "https://siebly.io/ai/exchange-state",
    "purpose": "Exchange-neutral runtime spec for private account, order, and position state workflows, including DCA/TP/SL managers.",
    "architecture": {
      "reusable": "order_account_position_state_machine",
      "lane": "ws_symbol_side_action_gated"
    },
    "role": "Primary implementation spec. Other artifacts support, specialize, or test this workflow; they do not replace it."
  },
  "antiTrace": {
    "normalPrivateWsBurstMustNot": [
      "private event -> REST hydrate",
      "REST acceptance -> trusted active order downgrade",
      "confirmed context -> recovery loop",
      "pending confirmation -> duplicate place",
      "execution fill -> open order confirmation"
    ]
  },
  "architecture": {
    "id": "order_account_position_state_machine",
    "lane": "ws_symbol_side_action_gated",
    "eventOrder": [
      "ingest_private_event",
      "apply_accountstate_update",
      "route_row_decision",
      "record_affected_scope",
      "schedule_event_burst_reconcile",
      "run_one_workflow_owner",
      "select_one_action_family",
      "record_pending_confirmation",
      "wait_for_trusted_state_or_recovery"
    ],
    "actionFamilyOrder": [
      "recovery",
      "cleanup",
      "protective",
      "dca",
      "noop",
      "blocked"
    ]
  },
  "specSteps": [
    "Save this exact prompt or spec in docs/AI_PROMPT.md (or docs/SPEC.md when that is the project standard), plus the prompt manifest, workflow state machine, private-event routing table, confirmation lifecycle, durable-context lifecycle, plan, and environment example before exchange calls.",
    "Verify installed SDK/API clients, request builders, custom ID fields, private topics, filters, error shapes, reconnect hooks, and shutdown methods from installed declarations/source before coding exchange adapters.",
    "Create the exchange-state runtime modules before exchange-specific adapters. Use the runtime kit when reusable scaffolding or typed module contracts are needed.",
    "After implementation, generate or run local tests from the applicable [behavior-contract fixtures](https://siebly.io/reference/glossary#behavior-contract-fixture) in the exchange-state and selected exchange [conformance packs](https://siebly.io/reference/glossary#conformance-pack) before enabling write-capable execution modes.",
    "Implement the event-driven private-state loop (ws_symbol_side_action_gated). Private events update [Account State](https://siebly.io/reference/glossary#accountstate), record the [affected scope](https://siebly.io/reference/glossary#affected-scope), give related events a short bounded window to arrive, and one [workflow owner](https://siebly.io/reference/glossary#workflow-owner) chooses one [action family](https://siebly.io/reference/glossary#action-family). Accepted mutations stay [pending confirmation](https://siebly.io/reference/glossary#pending-confirmation) until [private confirmation](https://siebly.io/reference/glossary#private-stream-confirmation) or [scoped recovery](https://siebly.io/reference/glossary#scoped-recovery), and REST state fetches happen only at trust boundaries.",
    "Each reconciliation pass chooses one [action family](https://siebly.io/reference/glossary#action-family) only: recover uncertain state, clean up stale app-owned orders, repair/place protection, place/repair DCA, do nothing, or wait because required evidence or configuration is missing.",
    "Do not enter REST recovery just because private order, execution, fill, or position events arrived. If the private stream is healthy and [Account State](https://siebly.io/reference/glossary#accountstate) has ingested the matching order evidence plus the matching position or account evidence, continue from private-stream account state without REST hydration. For products without exchange-provided position rows, locally derived position evidence fills the same role.",
    "Plan protective SL/TP before exposure-increasing DCA for a fresh managed position. DCA runs only after protection is trusted or scoped recovery proves the account state."
  ],
  "runtimeModules": [
    {
      "name": "OrderContextStore",
      "owns": "customOrderId -> order context",
      "contract": "Register context before submit, persist when configured, prune against trusted active orders, and never recover slot meaning by parsing custom IDs."
    },
    {
      "name": "AccountState",
      "owns": "subject trust for position state, active orders, fills, balances, filters, contexts, readiness, and sync/recovery flags",
      "contract": "Apply startup/recovery REST snapshots and private events into one account view. REST submit acceptance is coordination evidence only. Track order trust separately from position trust, filter trust, context trust, and readiness trust."
    },
    {
      "name": "PrivateEventRouter",
      "owns": "event classification, accountstate ingestion, affected-scope recording, and pending-confirmation matches",
      "contract": "Ingest private rows immediately, route known custom IDs through OrderContextStore, use bounded fallback scopes for ambiguous rows, and keep wallet-only events out of broad DCA/TP/SL planning unless configured."
    },
    {
      "name": "AffectedScopeQueue",
      "owns": "deduped product/symbol/side work items",
      "contract": "Canonicalize scope keys, dedupe repeats, preserve bounded follow-up reasons, and drain through one workflow owner."
    },
    {
      "name": "EventBurstReconciler",
      "owns": "short-delay reconciliation for private event bursts",
      "contract": "Apply events to AccountState immediately, record the exact product/symbol/side that changed, wait briefly so related events in the same burst can arrive, then run one reconciliation pass from the settled account state with a bounded max delay."
    },
    {
      "name": "ConfirmationTracker",
      "owns": "pending place/amend/cancel confirmations and in-flight custom IDs",
      "contract": "REST acceptance creates pending coordination state. Trusted private state or scoped recovery clears it. Timeout enters scoped recovery."
    },
    {
      "name": "RecoveryHydrator",
      "owns": "scoped REST hydration for untrusted subjects",
      "contract": "When recoveryRequired or sync-required state is set, hydrate positions, open orders, fills/executions, balances/readiness, replay buffered events, clear recovery on success, and requeue affected scopes."
    },
    {
      "name": "WorkflowOwner",
      "owns": "single active reconciliation/planning/submission pass",
      "contract": "Drain one affected scope at a time. If already active, record follow-up work and let the owner schedule one deferred pass."
    },
    {
      "name": "Planner",
      "owns": "desired managed slots and one selected action family",
      "contract": "Plan from trusted AccountState subjects. Products with exchange-provided position rows wait for matching position or account-state trust after order/fill events; products without exchange-provided position rows require trusted local position state. Each reconciliation pass chooses one [action family](https://siebly.io/reference/glossary#action-family) only: recover uncertain state, clean up stale app-owned orders, repair/place protection, place/repair DCA, do nothing, or wait because required evidence or configuration is missing."
    },
    {
      "name": "SubmissionExecutor",
      "owns": "preflight, context registration, exchange call dispatch, and provisional mutation records",
      "contract": "Pre-register contexts, preflight filters, risk geometry, and duplicate IDs, dispatch fresh SL/TP concurrently by default, and never unlock dependent action families from REST acceptance alone."
    },
    {
      "name": "ExchangeErrorClassifier",
      "owns": "deterministic block, benign race, retry/backoff, and scoped recovery decisions",
      "contract": "Classify by action, role, exchange code, current trusted state, and proof. Unknown mutation status enters scoped recovery."
    },
    {
      "name": "StructuredLogger",
      "owns": "redacted lifecycle, planner, submission, recovery, and shutdown logs",
      "contract": "Emit semantic counters and reasons without raw private payloads, secrets, signatures, listen keys, or raw [order intents](https://siebly.io/reference/glossary#order-intent)."
    }
  ],
  "behaviorContract": {
    "architecture": "ws_symbol_side_action_gated",
    "reusableArchitecture": "order_account_position_state_machine",
    "reusablePrimitives": [
      "custom-order-id-registry",
      "trusted-accountstate-boundary",
      "subject-level-trust-boundary",
      "position-state-update-trigger",
      "symbol-scope-auto-detect",
      "risk-geometry-validation",
      "ws-first-order-state",
      "recovery-hydrate-replan",
      "affected-scope-event-routing",
      "protective-sl-tp-dispatch",
      "mutation-confirmation-gating",
      "stale-mutation-noop-classification",
      "slot-convergence",
      "latch-lifecycle",
      "conformance-fixture-schema",
      "identity-chain-acceptance-gate"
    ],
    "managerLane": "simple_managed_slots",
    "accountStateSource": "Use the account-state layer as the in-memory account view. Feed it startup REST snapshots and private account events. Local submission outcomes are provisional coordination evidence and pending-confirmation inputs, not trusted open-order state. Do not build a second account-state reducer.",
    "subjectTrust": "Split trust by subject: order trust, position trust, filter trust, context trust, and readiness trust. A TP/SL/DCA order update proves only order trust; it does not prove position trust, position size, entry/cost basis, exposure, or replacement eligibility. For products with exchange-provided position rows, order/fill events are triggers; replacement planning waits for matching position trust from private position or account updates or scoped recovery. For products without exchange-provided position rows, derive local position state from fills/executions, fees, account events, balance updates, and documented matching rules before managed exits, exposure changes, or re-hedging.",
    "nativePositionReplanning": "For products with exchange-provided position rows, private order/fill updates are triggers, not replacement authority. Wait for the matching position or account-state update before re-protecting, re-DCA, cleanup/noop decisions, or run scoped recovery if the stream is broken.",
    "localPositionStateForNonNativeProducts": "For products without exchange-provided position rows, define local position state from fills/executions, fees, account events, balance updates, and documented matching rules. Do not apply derivative position lifecycle rules silently to balances alone.",
    "symbolScope": "If configuredSymbols is blank, absent, or empty, auto-detect eligible open positions. When symbols are configured, treat them as an allowlist: manage matching detected positions only and ignore or log out-of-scope private events without mutation. Sample symbols from docs, tests, prompts, or quickstarts are examples only; never use them as runtime defaults.",
    "defaultStrategyConfig": "For broad TP/SL/DCA position managers, use percentage-based strategy inputs by default: derive TP, DCA, and SL prices plus DCA size from trusted entry and position size before order planning. Absolute TP/DCA/SL prices or DCA quantities are advanced per-symbol operator overrides, not the primary config model. If allowed, they must be explicit, scoped, compatible with percentage defaults, and validated through the same filters and risk geometry. Startup validation rejects missing, invalid, contradictory, or non-finite strategy inputs before private clients, WebSocket subscriptions, hydration, or exchange mutation.",
    "riskGeometry": "For broad TP/SL/DCA position managers, use percentage-based strategy inputs by default: derive TP, DCA, and SL prices plus DCA size from trusted entry and position size before order planning. Absolute TP/DCA/SL prices or DCA quantities are advanced per-symbol operator overrides, not the primary config model. If allowed, they must be explicit, scoped, compatible with percentage defaults, and validated through the same filters and risk geometry. Startup validation rejects missing, invalid, contradictory, or non-finite strategy inputs before private clients, WebSocket subscriptions, hydration, or exchange mutation. Validate risk geometry before exchange mutation: for long positions, TP is above entry, DCA is below entry, and SL is below DCA; for short positions, TP is below entry, DCA is above entry, and SL is above DCA. Reject missing or invalid TP/DCA/SL strategy outputs before order placement. After derivation or override, quantize prices and quantities with hydrated exchange filters before final validation. After quantization, revalidate risk geometry, min/max, and notional rules; reject only if the quantized TP/DCA/SL values are equal, crossed, or otherwise violate geometry or filters before [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode)=DRY_RUN_PRIVATE, DEMO, TESTNET, or LIVE order placement. Risk geometry validation uses the trusted entry price or explicit operator override, hydrated tick/lot filters, and the managed side before any place/amend/cancel request.",
    "slotIdentity": "Use deterministic internal SlotKey values for strategy identity. Custom order IDs are lookup keys into an order-context store. Treat ID drift as non-actionable when role, step, side, quantity, price, trigger, exit semantics, and order kind still match.",
    "customOrderIdRegistry": "[Custom Order IDs](https://siebly.io/reference/glossary#custom-order-id) are lookup keys into one order-context registry/store. Generate a unique random ID, store context before submit, send the order with that ID, and resolve exchange echoes through the store. Follow exchange-specific prefix, length, and character rules only when required. Do not encode product, symbol, role, side, step, order kind, lifecycle, generation, recovery state, or other strategy state in the ID text.",
    "startupRecovery": "On startup, reconnect, restart, or recovery, hydrate trusted state first, manage orders proven app-owned by the runtime or persisted order store, optionally clean unknown scoped orders only when explicit config enables it, then rebuild expected slots from current positions.",
    "healthyStream": "During normal private event bursts, apply every event to [Account State](https://siebly.io/reference/glossary#accountstate) immediately, then use a short [event-burst wait](https://siebly.io/reference/glossary#event-burst-wait) before planning. Do not enter REST recovery just because private order, execution, fill, or position events arrived. If the private stream is healthy and [Account State](https://siebly.io/reference/glossary#accountstate) has ingested the matching order evidence plus the matching position or account evidence, continue from private-stream account state without REST hydration. For products without exchange-provided position rows, locally derived position evidence fills the same role.",
    "restBoundary": "Use scoped REST hydration for startup, restart, reconnect, stream exception or gap, missing prerequisites such as filters, unknown submit/cancel/amend outcomes, timeouts, sync-required or not-ready state, conflicting evidence, and explicit recovery. It is not the ordinary response to every healthy private event or ordinary pending confirmation.",
    "actionFamilyGate": "Each reconciliation pass chooses one [action family](https://siebly.io/reference/glossary#action-family) only: recover uncertain state, clean up stale app-owned orders, repair/place protection, place/repair DCA, do nothing, or wait because required evidence or configuration is missing. Normal cleanup, protection, and DCA work must not run from stale or unresolved state. Recovering uncertain state means blocking normal action families, running scoped hydration and buffered replay until local state is coherent with exchange state, then requeueing the [affected scope](https://siebly.io/reference/glossary#affected-scope).",
    "protectiveDispatch": "For fresh protection, pre-register SL and TP contexts and dispatch the separate SL and TP orders concurrently by default. If serialized protective mode is configured, submit SL before TP. DCA remains gated until required protection is trusted.",
    "pendingConfirmations": "Accepted place/amend/cancel responses create [pending confirmations](https://siebly.io/reference/glossary#pending-confirmation) and duplicate-submit protection, but they do not unlock dependent action families until [private confirmation](https://siebly.io/reference/glossary#private-stream-confirmation) or [scoped recovery](https://siebly.io/reference/glossary#scoped-recovery) proves account state.",
    "submissionTimeout": "Use a configurable bounded confirmation timeout for unknown REST/WS outcomes; 5 seconds is a reasonable default for generated managers before scoped recovery. Unknown outcomes enter scoped recovery rather than duplicate submit or trusted success.",
    "slotConvergence": "When an accountstate or exchange-overlay comparison helper is available, compare NormalizedOrder-to-NormalizedOrder: convert desired slot/request objects into the accountstate NormalizedOrder shape first, mapping local managedSide/managedStrategySide to strategySide, then compare them with active normalized orders. Do not pass strategy-local DesiredManagedOrder/request objects directly or hand-roll raw exchange-object equality. Regular fixed-price TP and DCA LIMIT slots converge by slot identity, order side, quantity, and limit price after tick/lot normalization. Close-position or close-on-trigger SL slots converge by slot identity, order side, trigger price, trigger source, close-position semantics, and stop kind; hydrated quantity, price, reduce-only, and default false-like fields are diagnostic unless current exchange docs make them actionable. Exchange echo fields, generated order IDs, lifecycle counters, default booleans, omitted trigger-only fields on regular orders, and display-only precision differences must not trigger cancel/recreate churn.",
    "liveRunFailureFixtures": "Core [behavior-contract fixtures](https://siebly.io/reference/glossary#behavior-contract-fixture) cover context write failure before submit, early private confirmation before REST accept, one-way side flips, pending duplicate place/cancel guards, and decimal/default-equivalent active-order convergence.",
    "semanticWsLogs": "Emit semantic logs for private stream ingestion and account-state application: private_ws_event_received, accountstate_event_applied, affected_scope_recorded, symbol_side_reconcile_started, action_family_selected, submission_accepted_pending_confirmation, private_ws_confirmation_applied, workflow_reconcile_complete.",
    "advancedLane": "Durable lifecycle epochs, replacement generations, and cross-process adoption belong to an advanced persistent strategy lane only when explicitly requested and tested.",
    "identityChainAcceptance": "Do not mark the implementation complete until three consecutive full identity-chain review passes produce no code, tests, fixtures, or documentation changes."
  },
  "subjectTrust": {
    "subjects": [
      "order trust",
      "position trust",
      "filter trust",
      "context trust",
      "readiness trust"
    ],
    "rules": [
      "Split trust by subject: order trust, position trust, filter trust, context trust, and readiness trust.",
      "A TP/SL/DCA order update proves only order trust; it does not prove position trust, position size, entry/cost basis, exposure, or replacement eligibility.",
      "For products with exchange-provided position rows, order/fill events are triggers; replacement planning waits for matching position trust from private position or account updates or scoped recovery.",
      "For products without exchange-provided position rows, derive local position state from fills/executions, fees, account events, balance updates, and documented matching rules before managed exits, exposure changes, or re-hedging."
    ]
  },
  "symbolScope": [
    "If configuredSymbols is blank, absent, or empty, auto-detect eligible open positions.",
    "When symbols are configured, treat them as an allowlist: manage matching detected positions only and ignore or log out-of-scope private events without mutation.",
    "Sample symbols from docs, tests, prompts, or quickstarts are examples only; never use them as runtime defaults."
  ],
  "riskGeometry": [
    "For broad TP/SL/DCA position managers, use percentage-based strategy inputs by default: derive TP, DCA, and SL prices plus DCA size from trusted entry and position size before order planning.",
    "Absolute TP/DCA/SL prices or DCA quantities are advanced per-symbol operator overrides, not the primary config model. If allowed, they must be explicit, scoped, compatible with percentage defaults, and validated through the same filters and risk geometry.",
    "Startup validation rejects missing, invalid, contradictory, or non-finite strategy inputs before private clients, WebSocket subscriptions, hydration, or exchange mutation.",
    "Validate risk geometry before exchange mutation: for long positions, TP is above entry, DCA is below entry, and SL is below DCA; for short positions, TP is below entry, DCA is above entry, and SL is above DCA.",
    "Reject missing or invalid TP/DCA/SL strategy outputs before order placement. After derivation or override, quantize prices and quantities with hydrated exchange filters before final validation.",
    "After quantization, revalidate risk geometry, min/max, and notional rules; reject only if the quantized TP/DCA/SL values are equal, crossed, or otherwise violate geometry or filters before [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode)=DRY_RUN_PRIVATE, DEMO, TESTNET, or LIVE order placement.",
    "Risk geometry validation uses the trusted entry price or explicit operator override, hydrated tick/lot filters, and the managed side before any place/amend/cancel request."
  ],
  "identityChainReview": {
    "cleanPassesRequired": 3,
    "chain": [
      "config scope",
      "product/environment/account",
      "symbol",
      "position mode",
      "managed side",
      "exchange position identity",
      "accountstate position state (native or locally derived)",
      "planner scope",
      "slot scope",
      "custom order ID",
      "durable context",
      "exchange request",
      "REST acceptance",
      "private order/execution/position event",
      "trusted active order",
      "subject trust state",
      "confirmation tracker",
      "recovery scope",
      "cleanup/protective/DCA replanning"
    ],
    "requiredRule": "Do not mark the implementation complete until three consecutive full identity-chain review passes produce no code, tests, fixtures, or documentation changes.",
    "passMustTrace": "Trace mismatched, stale, missing, contradictory, duplicated, late, recovered, and flat identities across every runtime boundary."
  },
  "liveRunFailureFixtures": [
    {
      "name": "context_write_failure_blocks_submission",
      "purpose": "A failed durable order-context write must block exchange submission.",
      "expected": "The planner records the context write failure, submits no order, creates no pending confirmation, and leaves the scope blocked or recovery-ready."
    },
    {
      "name": "private_confirmation_before_rest_accept_is_consumed",
      "purpose": "A private confirmation can arrive before the REST submit promise resolves.",
      "expected": "The early private confirmation is retained, matched to the later accepted submit response, and does not leave stale pending state or duplicate submission."
    },
    {
      "name": "side_flip_cleans_old_side_before_new_side_work",
      "purpose": "A one-way side flip cleans old-side app-owned orders before new-side protection or DCA.",
      "expected": "Cleanup for the old managed side settles first; new-side protective and DCA work stays blocked until the cleanup confirmation is trusted."
    },
    {
      "name": "pending_place_blocks_duplicate_place",
      "purpose": "A pending place confirmation blocks another place for the same slot.",
      "expected": "The planner emits no duplicate place intent while the matching place confirmation is still pending."
    },
    {
      "name": "pending_cancel_blocks_duplicate_cancel",
      "purpose": "A pending cancel confirmation blocks another cancel for the same target.",
      "expected": "The planner emits no duplicate cancel intent while the matching cancel confirmation is still pending."
    },
    {
      "name": "decimal_and_default_equivalent_active_order_does_not_replace",
      "purpose": "Equivalent active orders do not churn because of display precision or hydrated defaults.",
      "expected": "Desired-vs-active comparison normalizes decimals and non-actionable exchange defaults by order kind, then suppresses cancel/recreate when actionable slot fields match."
    }
  ],
  "finalAcceptanceGate": [
    "Trace the full identity chain: config scope -> product/environment/account -> symbol -> position mode -> managed side -> exchange position identity -> accountstate position state (native or locally derived) -> planner scope -> slot scope -> custom order ID -> durable context -> exchange request -> REST acceptance -> private order/execution/position event -> trusted active order -> subject trust state -> confirmation tracker -> recovery scope -> cleanup/protective/DCA replanning.",
    "Do not mark the implementation complete until three consecutive full identity-chain review passes produce no code, tests, fixtures, or documentation changes.",
    "Trace mismatched, stale, missing, contradictory, duplicated, late, recovered, and flat identities across every runtime boundary.",
    "Every accepted lifecycle claim needs an executable fixture; behaviors without fixtures must be listed as unsupported or unverified.",
    "Core [behavior-contract fixtures](https://siebly.io/reference/glossary#behavior-contract-fixture) cover context write failure before submit, early private confirmation before REST accept, one-way side flips, pending duplicate place/cancel guards, and decimal/default-equivalent active-order convergence.",
    "The [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode)=DEMO, TESTNET, or LIVE submission paths are implemented for order-capable projects where supported, but they remain disabled until the required identity-chain, subject-trust, symbol-scope, and risk-geometry fixtures exist and pass."
  ],
  "forbiddenDesigns": [
    "Do not encode product, symbol, role, side, step, order kind, lifecycle, generation, recovery state, or other strategy state in the ID text.",
    "Do not recover strategy meaning from exchange-visible IDs after restart for the default manager.",
    "Do not add a generic custom-ID prefix unless the exchange or SDK requires one.",
    "Do not use conditional/Algo/trigger orders for ordinary fixed-price TP targets unless that behavior is explicitly configured and supported.",
    "Fixed TP and DCA use regular LIMIT orders. A DCA LIMIT may fill immediately at limit-or-better or rest at the submitted limit price; both are normal LIMIT behavior. Do not add maker-only, post-only, mark-price, quote-freshness, marketability, or forced-maker gates unless explicitly configured.",
    "Do not use REST hydration as the normal reaction to healthy private account events, healthy order/fill/position events, or ordinary pending confirmations.",
    "Do not replace all slots because one slot changed.",
    "Do not replace any slot solely because a client ID, lifecycle counter, generation value, or in-memory metadata changed.",
    "Do not pause protective TP/SL management because an exposure-increasing DCA order hit max-position, leverage, notional, or equivalent risk limits.",
    "Do not compare raw hydrated order objects field-by-field; compare managed slots by the actionable fields for that order kind.",
    "When an accountstate or exchange-overlay comparison helper is available, compare NormalizedOrder-to-NormalizedOrder: convert desired slot/request objects into the accountstate NormalizedOrder shape first, mapping local managedSide/managedStrategySide to strategySide, then compare them with active normalized orders. Do not pass strategy-local DesiredManagedOrder/request objects directly or hand-roll raw exchange-object equality.",
    "Do not submit protective exits and exposure-increasing DCA in the same first workflow pass for a newly discovered position.",
    "Do not let accepted submit responses unlock dependent action families until the private stream or scoped recovery hydration confirms account state."
  ],
  "notConfirmations": [
    {
      "evidence": "REST place/amend/cancel acceptance",
      "route": "record or match [Pending Confirmation](https://siebly.io/reference/glossary#pending-confirmation)",
      "reason": "Transport or request success does not prove active open-order state."
    },
    {
      "evidence": "Terminal order row",
      "route": "clear or terminalize the matching latch",
      "reason": "Cancelled, rejected, filled, deactivated, triggered, or equivalent rows are not active slot evidence."
    },
    {
      "evidence": "Execution or fill row",
      "route": "update fills and record the affected scope",
      "reason": "Fill evidence can change position/order state but does not confirm active open-order existence or [Position Trust](https://siebly.io/reference/glossary#position-trust)."
    },
    {
      "evidence": "Private order update",
      "route": "update [Order Trust](https://siebly.io/reference/glossary#order-trust), match pending confirmations, and record the affected scope",
      "reason": "[Order Trust](https://siebly.io/reference/glossary#order-trust) is not [Position Trust](https://siebly.io/reference/glossary#position-trust). When the exchange provides position rows, replacement planning waits for a matching position or account-state update or [Scoped Recovery](https://siebly.io/reference/glossary#scoped-recovery)."
    },
    {
      "evidence": "Private stream open or subscription event",
      "route": "update transport readiness only",
      "reason": "Account State readiness still needs hydration, replay, and private account-state evidence."
    },
    {
      "evidence": "Active DCA order for the current step",
      "route": "hold dependent DCA work",
      "reason": "An active DCA order is not permission to place the next DCA step."
    }
  ],
  "recoveryRule": "When recoveryRequired or sync-required state is set, hydrate/replay/replan before normal cleanup, protective, or DCA actions. Pre-hydration cleanup is an advanced exception, not the generated default.",
  "confirmationLifecycle": [
    "Register order context before submit.",
    "Record in-flight mutation before the exchange call.",
    "Ingest private confirmations even if they arrive before REST settles.",
    "REST acceptance creates or matches pending confirmation only.",
    "Trusted private state or scoped recovery clears pending confirmation.",
    "Timeout or ambiguity enters scoped recovery instead of duplicate submit."
  ],
  "requiredChecks": [
    {
      "name": "startup_hydrated_position_queues_work_before_service_ready",
      "purpose": "REST-hydrated open positions queue work before service readiness.",
      "expected": "Hydration records affected scopes and a reconciliation pass runs before service_ready."
    },
    {
      "name": "private_event_records_scope_and_schedules_reconcile",
      "purpose": "Private event ingestion leads to scoped workflow scheduling.",
      "expected": "The event applies to accountstate, records the precise or bounded affected scope, and schedules reconciliation."
    },
    {
      "name": "private_event_burst_waits_for_related_events",
      "purpose": "Account State ingestion is immediate while workflow execution briefly waits for related events in the same burst.",
      "expected": "Multiple related events update accountstate immediately, then produce one reconciliation pass after related events have had a short bounded chance to arrive."
    },
    {
      "name": "custom_order_id_registry_routes_exchange_echoes",
      "purpose": "Exchange-visible custom IDs are lookup keys into one order-context store.",
      "expected": "Private order echoes map to slot, scope, role, and pending action through the registry without parsing the ID string."
    },
    {
      "name": "pending_confirmation_does_not_unlock_dependent_action_family",
      "purpose": "Accepted mutation responses are provisional and cannot unlock dependent action families.",
      "expected": "Dependent action families wait for trusted private stream state or scoped recovery hydration."
    },
    {
      "name": "context_write_failure_blocks_submission",
      "purpose": "A failed durable order-context write must block exchange submission.",
      "expected": "The planner records the context write failure, submits no order, creates no pending confirmation, and leaves the scope blocked or recovery-ready."
    },
    {
      "name": "private_confirmation_before_rest_accept_is_consumed",
      "purpose": "A private confirmation can arrive before the REST submit promise resolves.",
      "expected": "The early private confirmation is retained, matched to the later accepted submit response, and does not leave stale pending state or duplicate submission."
    },
    {
      "name": "side_flip_cleans_old_side_before_new_side_work",
      "purpose": "A one-way side flip cleans old-side app-owned orders before new-side protection or DCA.",
      "expected": "Cleanup for the old managed side settles first; new-side protective and DCA work stays blocked until the cleanup confirmation is trusted."
    },
    {
      "name": "pending_place_blocks_duplicate_place",
      "purpose": "A pending place confirmation blocks another place for the same slot.",
      "expected": "The planner emits no duplicate place intent while the matching place confirmation is still pending."
    },
    {
      "name": "pending_cancel_blocks_duplicate_cancel",
      "purpose": "A pending cancel confirmation blocks another cancel for the same target.",
      "expected": "The planner emits no duplicate cancel intent while the matching cancel confirmation is still pending."
    },
    {
      "name": "decimal_and_default_equivalent_active_order_does_not_replace",
      "purpose": "Equivalent active orders do not churn because of display precision or hydrated defaults.",
      "expected": "Desired-vs-active comparison normalizes decimals and non-actionable exchange defaults by order kind, then suppresses cancel/recreate when actionable slot fields match."
    },
    {
      "name": "terminal_event_during_cancel_or_amend_is_benign_race",
      "purpose": "Terminal private evidence can make later missing-target cancel/amend errors benign.",
      "expected": "The target is terminalized and no duplicate recovery loop starts when proof exists."
    },
    {
      "name": "reconnect_queues_known_scopes_for_recovery_even_when_quiet",
      "purpose": "Reconnect schedules recovery even without a later private event.",
      "expected": "Known active scopes enter scoped hydration and replan from trusted state before new exposure resumes."
    },
    {
      "name": "slot_filter_failures_are_blocked_not_silent_or_retried",
      "purpose": "Exchange filters and local sizing gates produce explicit blocks.",
      "expected": "Min quantity, min notional, zero-size, or missing-filter cases log a block reason and do not spin retries."
    },
    {
      "name": "flat_position_cleans_app_owned_orders",
      "purpose": "A flat position cleans remaining managed orders.",
      "expected": "Only app-owned orders in the affected scope are cancelled or terminalized; unowned orders are untouched."
    }
  ],
  "completeRequiredChecks": [
    "startup_hydrated_position_queues_work_before_service_ready",
    "private_event_records_scope_and_schedules_reconcile",
    "private_event_burst_waits_for_related_events",
    "custom_order_id_registry_routes_exchange_echoes",
    "pending_confirmation_does_not_unlock_dependent_action_family",
    "context_write_failure_blocks_submission",
    "private_confirmation_before_rest_accept_is_consumed",
    "side_flip_cleans_old_side_before_new_side_work",
    "pending_place_blocks_duplicate_place",
    "pending_cancel_blocks_duplicate_cancel",
    "decimal_and_default_equivalent_active_order_does_not_replace",
    "terminal_event_during_cancel_or_amend_is_benign_race",
    "reconnect_queues_known_scopes_for_recovery_even_when_quiet",
    "slot_filter_failures_are_blocked_not_silent_or_retried",
    "flat_position_cleans_app_owned_orders",
    "rest_acceptance_does_not_downgrade_trusted_private_order",
    "terminal_order_status_is_not_open_order_confirmation",
    "execution_fill_is_not_open_order_confirmation",
    "default_dca_active_order_does_not_advance_next_step",
    "startup_adopts_active_contexts_and_prunes_absent_contexts",
    "ambiguous_event_routes_bounded_fallback_then_exact_scope",
    "pending_confirmation_timeout_enters_scoped_recovery",
    "recovery_hydration_blocks_normal_actions_until_trust_restored",
    "blank_symbols_auto_detects_open_positions",
    "absent_symbols_auto_detects_open_positions",
    "blank_symbols_no_positions_noops_without_mutation",
    "symbol_allowlist_manages_matching_detected_positions_only",
    "sample_symbol_not_used_as_runtime_default",
    "configured_symbol_allowlist_blocks_out_of_scope_events",
    "order_confirmation_does_not_imply_position_trust",
    "native_position_dca_fill_waits_for_position_update",
    "native_position_tp_sl_fill_waits_for_position_update",
    "spot_local_position_state_substitutes_for_native_position_rows",
    "long_dca_equal_sl_rejected",
    "short_dca_equal_sl_rejected",
    "dry_run_status_doctor_cannot_submit_live_orders"
  ],
  "runnerContract": {
    "language": "typescript",
    "fixtureSource": "JSON fixture set with a TypeScript runner contract for local executable tests",
    "entrypoint": "https://siebly.io/.well-known/conformance/exchange-state-management/v1/runner.ts",
    "exportedTypes": [
      "ExchangeStateFixture",
      "ExchangeStateFixtureStep",
      "ExchangeStateFixtureExpectation",
      "ExchangeStateHarness",
      "ExchangeStateFixtureReport",
      "runExchangeStateManagementFixtures"
    ],
    "requiredFixtureFields": [
      "name",
      "intent",
      "scope",
      "initial",
      "steps",
      "expect",
      "forbid"
    ],
    "expectationSemantics": [
      "Object expectations are partial matches.",
      "Array expectations are containment matches that consume one observed item per expected item; an empty expected array requires an empty observed array.",
      "Primitive expectations are exact matches after stable JSON normalization.",
      "Forbidden actions are read from observed.forbiddenObserved and matched against fixture.forbid."
    ]
  },
  "exchangeOverlays": {
    "binance": "https://siebly.io/.well-known/integration-kits/binance-position-manager/latest.json",
    "bybit": "https://siebly.io/.well-known/integration-kits/bybit-position-manager/latest.json"
  }
}
