{
  "format": "siebly-agent-implementation-manifest/v1",
  "id": "bybit-position-manager",
  "title": "Bybit Position Management Agent Manifest",
  "task": "bybit-position-manager",
  "version": 1,
  "lastReviewed": "2026-05-12",
  "purpose": "Implementation manifest for coding agents building a Bybit Spot and linear position manager.",
  "canonicalArtifacts": {
    "manifest": "https://siebly.io/.well-known/agent-manifests/bybit-position-manager/latest.json",
    "coreManifest": "https://siebly.io/.well-known/agent-manifests/exchange-state-management/latest.json",
    "coreRuntimeKit": "https://siebly.io/.well-known/runtime-kits/exchange-state-management/v1/index.json",
    "coreRuntimeTypes": "https://siebly.io/.well-known/runtime-kits/exchange-state-management/v1/types.ts",
    "coreIntegrationKit": "https://siebly.io/.well-known/integration-kits/exchange-state-management/latest.json",
    "coreConformancePack": "https://siebly.io/.well-known/conformance/exchange-state-management/latest.json",
    "coreConformanceSchema": "https://siebly.io/.well-known/conformance/exchange-state-management/v1/schema.json",
    "coreConformanceFixtures": "https://siebly.io/.well-known/conformance/exchange-state-management/v1/fixtures.json",
    "coreConformanceRunner": "https://siebly.io/.well-known/conformance/exchange-state-management/v1/runner.ts",
    "specGuide": "https://siebly.io/ai/exchange-state",
    "integrationKit": "https://siebly.io/.well-known/integration-kits/bybit-position-manager/latest.json",
    "conformancePack": "https://siebly.io/.well-known/conformance/bybit-position-manager/latest.json",
    "guide": "https://siebly.io/ai/exchange-state/bybit",
    "recipe": "https://siebly.io/.well-known/recipes/bybit-position-manager.json",
    "artifactOrder": [
      "https://siebly.io/ai/exchange-state",
      "https://siebly.io/.well-known/prompts/bybit-position-manager/latest.md",
      "https://siebly.io/.well-known/recipes/bybit-position-manager.json",
      "https://siebly.io/ai/exchange-state/bybit",
      "https://siebly.io/.well-known/integration-kits/bybit-position-manager/latest.json",
      "https://siebly.io/.well-known/agent-manifests/bybit-position-manager/latest.json",
      "https://siebly.io/.well-known/agent-manifests/exchange-state-management/latest.json",
      "https://siebly.io/.well-known/runtime-kits/exchange-state-management/v1/index.json",
      "https://siebly.io/.well-known/runtime-kits/exchange-state-management/v1/types.ts",
      "https://siebly.io/.well-known/integration-kits/exchange-state-management/latest.json",
      "https://siebly.io/.well-known/conformance/exchange-state-management/latest.json",
      "https://siebly.io/.well-known/conformance/exchange-state-management/v1/schema.json",
      "https://siebly.io/.well-known/conformance/exchange-state-management/v1/fixtures.json",
      "https://siebly.io/.well-known/conformance/exchange-state-management/v1/runner.ts",
      "https://siebly.io/.well-known/conformance/bybit-position-manager/latest.json"
    ],
    "fallbackSources": [
      "https://siebly.io/sdk/bybit/javascript",
      "https://siebly.io/sdk/bybit/javascript/tutorial",
      "https://siebly.io/llms-tasks.txt",
      "https://siebly.io/.well-known/siebly-sdk-catalog.json",
      "https://github.com/tiagosiebler/bybit-api",
      "https://github.com/sieblyio/crypto-api-examples/tree/master/examples/Bybit"
    ]
  },
  "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."
  },
  "authorityOrder": [
    "When this manifest is explicitly selected, use it as an advanced reusable-runtime checklist; the default build path remains the primary spec and selected exchange recipe.",
    "Use the exchange-state runtime kit for reusable module contracts when needed; it is not a required first read for simple implementations.",
    "Exchange State Management Integration Kit and Bybit Position Management Integration Kit provide runtime rules, field matrices, event policy, recovery state, retCode handling, command safety, and fixture names.",
    "Exchange State Management Conformance Pack and Bybit Position Management Conformance Pack are post-implementation validation sources. After implementation, generate or run local [behavior-contract fixtures](https://siebly.io/reference/glossary#behavior-contract-fixture) from Exchange State Management Conformance Pack and Bybit Position Management Conformance Pack before enabling EXECUTION_MODE=DEMO, TESTNET, or LIVE submission.",
    "Use the recipe and guide for the default build path and explanatory context.",
    "Installed bybit-api package declarations and current Bybit API docs are the final authority for exact request shapes."
  ],
  "package": {
    "ecosystem": "npm",
    "name": "bybit-api",
    "install": "npm install bybit-api",
    "versionPolicy": "Use the latest available bybit-api package. Record the installed version in the README dependency notes; installed declarations and current docs are the authority for exact request shapes.",
    "docs": "https://siebly.io/sdk/bybit/javascript",
    "repository": "https://github.com/tiagosiebler/bybit-api"
  },
  "requiredFirstSteps": [
    "Install the latest available dependencies and record the installed bybit-api package version as metadata.",
    "Run or create the local SDK/API surface check script.",
    "Write README SDK surface notes with exact methods, types, files, examples, and package version checked.",
    "If using this manifest, generate a short implementation checklist from it and the integration kit before editing exchange code.",
    "Implement EXECUTION_MODE=DRY_RUN_PRIVATE planning first. After implementation, run applicable [behavior-contract fixtures](https://siebly.io/reference/glossary#behavior-contract-fixture) before enabling EXECUTION_MODE=DEMO, TESTNET, or LIVE submission. [conformance packs](https://siebly.io/reference/glossary#conformance-pack) are the machine-readable fixture sources for these checks."
  ],
  "spec": [
    "Install the latest available bybit-api package and record the installed version as SDK surface metadata only.",
    "Verify RestClientV5, WebsocketClient, linear ws.subscribeV5(['order', 'execution', 'position', 'wallet'], 'linear', true), Spot private subscription shape, lifecycle events, closeAll(true), and REST method names from installed declarations before coding.",
    "Build path: primary spec -> selected exchange recipe -> integration kit for exact fields, event shapes, and errors -> applicable [behavior-contract fixtures](https://siebly.io/reference/glossary#behavior-contract-fixture) after implementation. [conformance packs](https://siebly.io/reference/glossary#conformance-pack) are machine-readable fixture sources, not first-read design prose. Use manifests and runtime kits only for advanced reusable-runtime scaffolding; use general SDK pages, examples, catalogs, and llms-full only when installed package source leaves a method or request shape unresolved.",
    "Start with startup/recovery hydration and EXECUTION_MODE=DRY_RUN_PRIVATE DCA/TP/SL intents. The finished project must implement DEMO, TESTNET, or LIVE order placement, amend, and cancel for the selected Bybit product where supported, but exchange writes remain disabled unless EXECUTION_MODE=DEMO, TESTNET, or LIVE and all matching gates pass.",
    "Startup hydration loads filters, positions or inputs for local position tracking, open orders, fills or executions, wallet or margin state, account mode, risk/readiness state, and stored contexts.",
    "For Bybit linear startup/restart hydration, call getPositionInfo with settleCoin for each configured settlement coin, defaulting to USDT and USDC. Never call getPositionInfo with only category=linear; use symbol only for deliberately narrow scoped recovery, and filter configuredSymbols locally after settleCoin hydration.",
    "For Bybit linear active-order startup/restart hydration, either capture and preserve a redacted raw getActiveOrders({ category: \"linear\", settleCoin, openOnly: 0 }) response proving regular and conditional StopOrder rows are both returned for the account mode, or defensively fetch orderFilter=\"Order\" and orderFilter=\"StopOrder\" separately for each settleCoin and merge by orderId/orderLinkId.",
    "Connect private account-level WebSockets only after scope and credentials are explicit. Buffer private events until startup/recovery hydration and replay are complete. 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.",
    "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.",
    "Prefer RestClientV5 throwExceptions=true for order, amend, cancel, and pre-check workflows; if disabled, classify every response by retCode === 0.",
    "Use [orderLinkId](https://siebly.io/reference/glossary#custom-order-id) values as [Custom Order IDs](https://siebly.io/reference/glossary#custom-order-id) for DCA, TP, SL, cancel, and amend workflows. Pre-register context before EXECUTION_MODE=DRY_RUN_PRIVATE, DEMO, TESTNET, or LIVE submission, persist context by default for order-capable runs, then use private events with orderLinkId to look up SlotKey/intent context.",
    "Distinguish operatorPaused, recoveryRequired, syncRequired subjects, and privateStreamReady. RecoveryRequired or syncRequired blocks normal cleanup, protective, DCA, and [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode)=DEMO, TESTNET, or LIVE submission until scoped REST hydration plus buffered replay restores trusted account state. Deterministic stale-target no-ops avoid entering recovery when account state already proves convergence.",
    "For linear managed SL, default to close-all Market StopOrder: qty=\"0\", reduceOnly=true, closeOnTrigger=true, triggerDirection 2 for long SL and 1 for short SL, triggerBy=MarkPrice unless configured, positionIdx matching the position side, and no price field.",
    "Normalize hydrated Bybit active-order defaults by slot kind before desired-vs-active comparison and classify slots as keep, amend, cancel_place, cancel, or place so accepted app-owned orders converge to intentCount=0 instead of cancel/recreate churn.",
    "Run each EXECUTION_MODE=DRY_RUN_PRIVATE, DEMO, TESTNET, or LIVE reconciliation pass through one action family: resync uncertain state first if required, cleanup, concurrent protective SL/TP place or amend after contexts are registered, pending confirmation, and later DCA only if private stream evidence or scoped recovery hydration confirms the position still exists. EXECUTION_MODE=DRY_RUN_PRIVATE records candidates only. DEMO, TESTNET, or LIVE may submit exchange mutations after all gates pass.",
    "Map EXECUTION_MODE=DEMO to demoTrading=true and testnet=false, and map EXECUTION_MODE=TESTNET to Bybit testnet routing. Do not use Bybit WebSocket API order commands for demo endpoint trading."
  ],
  "criticalWriteConformance": {
    "conformancePack": "https://siebly.io/.well-known/conformance/bybit-position-manager/latest.json",
    "mustPassBeforeLive": [
      "bybit_position_open_reacts_without_timer",
      "bybit_private_stream_exception_prevents_service_ready",
      "bybit_private_topic_updates_can_complete_readiness_without_args_ack",
      "bybit_subscribe_response_req_id_topics_can_mark_ready",
      "bybit_private_stream_reconnect_clears_exception_before_ack_readiness",
      "bybit_private_position_replay_drives_planning_without_rest_rehydrate",
      "bybit_normal_state_check_uses_accountstate_not_rest",
      "bybit_reconnect_uses_rest_hydration_before_resume",
      "bybit_private_event_burst_groups_to_single_reconcile",
      "bybit_order_execution_position_wallet_out_of_order_converges",
      "bybit_accepted_submissions_settle_to_zero_intents",
      "bybit_golden_live_convergence_trace_settles_after_deferred_dca",
      "bybit_position_open_places_protective_then_dca_after_ws_confirmation",
      "bybit_pending_confirmation_does_not_unlock_dependent_action_family",
      "bybit_semantic_ws_logs_emitted_after_ingest",
      "bybit_symbol_side_queue_groups_private_event_burst",
      "bybit_order_active_confirmation_before_rest_acceptance_no_stale_pending",
      "bybit_rest_acceptance_does_not_downgrade_trusted_private_order",
      "bybit_terminal_order_status_is_not_open_order_confirmation",
      "bybit_execution_fill_is_not_open_order_confirmation",
      "bybit_position_close_between_exit_action_and_dca_skips_dca",
      "bybit_dca_never_submitted_from_stale_pre_close_plan",
      "bybit_conditional_sl_requires_trigger_direction",
      "bybit_hedge_normalized_position_idx_maps_to_bybit_request_idx",
      "bybit_one_way_short_uses_position_idx_zero",
      "bybit_tp_is_reduce_only_limit_with_expected_time_in_force",
      "bybit_position_update_timestamp_does_not_rotate_slots",
      "bybit_full_close_cleanup_cancels_only_app_owned_orders",
      "bybit_one_way_short_then_long_fresh_scope_cleans_old_side",
      "bybit_hedge_same_symbol_sides_do_not_cross_cancel",
      "bybit_startup_wipe_rebuild_cancels_app_orders_before_rebuild",
      "bybit_startup_adopts_active_contexts_and_prunes_absent_contexts",
      "bybit_default_dca_active_order_does_not_advance_next_step",
      "bybit_ambiguous_event_routes_bounded_fallback_then_exact_scope",
      "bybit_linear_close_all_sl_request_is_valid",
      "bybit_same_side_position_add_amends_same_slots_without_cancel_first",
      "bybit_same_slot_update_submits_amends_without_cancel_gap",
      "bybit_cancel_first_replacement_converges_before_place",
      "bybit_amend_rejection_enters_recovery_without_blind_cancel_place",
      "bybit_retcode_nonzero_blocks_failed_slot_or_enters_recovery",
      "bybit_submission_rejection_requires_resync_without_permanent_pause",
      "bybit_order_link_id_context_lookup_routes_private_event",
      "bybit_lost_order_link_id_registry_rebuilds_from_trusted_state",
      "bybit_protective_sl_tp_pre_registered_and_dispatched_concurrently_by_default",
      "bybit_partial_protective_reject_blocks_failed_slot_and_surfaces_error",
      "bybit_unknown_submit_outcome_enters_recovery_after_configured_timeout",
      "bybit_recovery_required_blocks_normal_actions_until_hydration_succeeds",
      "bybit_hydrated_defaults_do_not_churn",
      "bybit_deferred_replan_owner_only",
      "bybit_sub_unit_tick_formats_trigger_price_correctly",
      "bybit_single_digit_price_tick_formats_correctly",
      "bybit_large_price_tick_formats_correctly",
      "bybit_integer_quantity_lot_formats_correctly",
      "bybit_fractional_quantity_lot_formats_correctly",
      "bybit_rejected_order_link_id_is_not_retried_without_state_change",
      "bybit_rejected_order_link_id_can_retry_after_request_change",
      "bybit_duplicate_order_link_id_preflights_place_intent_without_blocking_cleanup",
      "bybit_stale_context_wrong_position_idx_enters_recovery",
      "bybit_active_app_order_without_context_refuses_mutation_or_adopts_explicitly",
      "bybit_context_without_active_order_is_inconclusive_until_recovery",
      "bybit_flat_hedge_row_with_opposite_open_does_not_emit_closed_notice",
      "bybit_one_way_flat_row_uses_previous_side_identity",
      "bybit_completed_dca_state_survives_restart_without_active_order",
      "bybit_filter_only_recovery_does_not_prune_contexts",
      "bybit_open_order_recovery_prunes_absent_contexts",
      "bybit_late_private_event_routes_to_current_or_terminal_scope",
      "bybit_duplicate_slots_same_role_step_resolve_without_extra_place",
      "bybit_pending_confirmation_blocks_duplicate_dca_place",
      "bybit_terminal_dca_fill_then_planner_tick_reprotects_only_after_position_update",
      "bybit_position_mode_auto_detects_observed_exchange_identity",
      "bybit_hedge_risk_blocks_dca_but_allows_protective_repair"
    ]
  },
  "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."
  },
  "identityMismatchPolicy": {
    "scope": "Trace category, environment, account, symbol, position mode, managed side, positionIdx, durable context, orderLinkId, active order, private order/execution/position event, confirmation tracker, recovery scope, and replanning together.",
    "accountAndPositionMode": [
      "Prefer auto-detection from observed exchange/accountstate identity when the evidence is clear.",
      "Do not block only because configured mode text differs from observed mode; record the mismatch and follow observed identity.",
      "Refuse mutation only while mode evidence is unresolved, stale, or contradictory."
    ],
    "durableContext": [
      "Saved durable context without matching trusted active-order evidence remains unresolved until scoped active-order recovery confirms absence or terminal state.",
      "Active app-owned order without saved context refuses mutation unless an explicit adoption path is configured and fixture-proven.",
      "Terminal order rows are not active protective confirmations.",
      "Filled DCA steps are completed step identity, not active orders, and completed DCA state survives restart."
    ],
    "positionRowsAndRisk": [
      "Flat hedge private position rows are state rows, not close lifecycle events unless previous state was open for that same scoped side.",
      "A flat row on one hedge side must not emit a close notice or cleanup for the opposite open side.",
      "One-way flat rows require previous managed-side identity before close/noop decisions.",
      "Hedge-risk controls block exposure-increasing DCA, not protective TP/SL repair."
    ],
    "recoveryScopes": [
      "Filter-only recovery restores filters and must not prune durable order contexts.",
      "Open-order recovery may confirm or prune contexts only with explicit active-order evidence.",
      "Startup mutation waits until private stream confirmation can be observed."
    ],
    "fixtureNames": [
      "bybit_stale_context_wrong_position_idx_enters_recovery",
      "bybit_active_app_order_without_context_refuses_mutation_or_adopts_explicitly",
      "bybit_context_without_active_order_is_inconclusive_until_recovery",
      "bybit_flat_hedge_row_with_opposite_open_does_not_emit_closed_notice",
      "bybit_one_way_flat_row_uses_previous_side_identity",
      "bybit_completed_dca_state_survives_restart_without_active_order",
      "bybit_filter_only_recovery_does_not_prune_contexts",
      "bybit_open_order_recovery_prunes_absent_contexts",
      "bybit_late_private_event_routes_to_current_or_terminal_scope",
      "bybit_duplicate_slots_same_role_step_resolve_without_extra_place",
      "bybit_pending_confirmation_blocks_duplicate_dca_place",
      "bybit_terminal_dca_fill_then_planner_tick_reprotects_only_after_position_update",
      "bybit_position_mode_auto_detects_observed_exchange_identity",
      "bybit_hedge_risk_blocks_dca_but_allows_protective_repair"
    ]
  },
  "customOrderIdRegistry": "order-context registry maps [orderLinkId](https://siebly.io/reference/glossary#custom-order-id) values to SlotKey/intent context before submission; private events echo orderLinkId to resolve context without parsing ID text.",
  "runtimeContract": {
    "restHydrationReasons": [
      "startup",
      "reconnect",
      "reconnected",
      "exception",
      "sync_required",
      "submission_recovery",
      "conflicting_state"
    ],
    "normalPrivateEventBoundary": "After readiness, wait briefly for related private events in the same burst, then replay them into trusted accountstate without full REST hydration unless a recovery reason is present.",
    "recoveryState": {
      "recoveryRequired": true,
      "operatorPaused": false,
      "blocksNewExposure": true,
      "blocksNormalPhasesUntilHydrated": true,
      "requiresScopedHydration": true,
      "clearsBy": [
        "scoped_rest_hydration",
        "buffered_private_event_replay",
        "coherent_accountstate_read"
      ]
    },
    "submissionActionFamilies": [
      "recovery",
      "cleanup",
      "protective",
      "dca",
      "noop",
      "blocked"
    ],
    "goldenRuntimeTrace": {
      "startup": [
        "subscribe private order/execution/position/wallet topics",
        "REST hydrate instruments, account mode, wallet, positions, active orders, executions, and risk limits",
        "wait for private topic ack or topic updates",
        "replay buffered private events",
        "emit private_ws_event_received/accountstate_event_applied/affected_scope_recorded",
        "plan one affected symbol-side from accountstate",
        "emit cleanup candidate or submit cleanup action if write mode permits",
        "pre-register protective SL/TP contexts and record or dispatch separate SL/TP orders concurrently in protective action family",
        "wait for private stream confirmation or scoped recovery hydration",
        "record or submit DCA in a later DCA action family only if the position still exists and still needs DCA",
        "planner_finished intentCount=0"
      ],
      "normalPrivateBurst": [
        "buffer order/execution/position/wallet events for about 25ms",
        "replay burst into accountstate without REST hydration while trusted",
        "emit semantic WS/accountstate logs and record the affected symbol-side scope",
        "plan one affected symbol-side from accountstate",
        "amend same-slot TP/SL/DCA where possible",
        "planner_finished intentCount=0"
      ],
      "reconnectOrException": [
        "mark stream not ready and set recoveryRequired or syncRequired",
        "keep buffering private events where possible",
        "REST hydrate affected category/symbol/positionIdx scope",
        "wait for topic ack or private topic update",
        "replay buffered events",
        "resume only when accountstate is coherent"
      ]
    },
    "redFlagLogs": [
      "repeated nonzero intentCount with no accountstate change",
      "repeated cancel/place for the same logical slot when amend would work",
      "repeated REST hydration on normal private account-event bursts",
      "positionIdx:0 request for normalized hedge LONG or SHORT",
      "product_paused after a submission rejection without explicit operator stop",
      "duplicate orderLinkId after cancel-first replacement",
      "role, step, side, or strategy state encoded into orderLinkId",
      "DCA submitted from a plan made before an intervening close or fill",
      "app-owned cleanup blocked by duplicate or recent place guards",
      "accepted submit response immediately unlocks DCA before private stream confirmation",
      "workflow_finished logged from a stale pre-recovery snapshot"
    ]
  },
  "sdkSurfaceCheckScript": {
    "filename": "scripts/verify-bybit-position-manager-surfaces.mjs",
    "command": "node scripts/verify-bybit-position-manager-surfaces.mjs",
    "scriptLines": [
      "import { createRequire } from 'node:module';",
      "const require = createRequire(import.meta.url);",
      "const pkg = require('bybit-api/package.json');",
      "const bybit = await import('bybit-api');",
      "const requiredExports = ['RestClientV5', 'WebsocketClient'];",
      "const report = {",
      "  package: 'bybit-api',",
      "  installedVersion: pkg.version,",
      "  exports: Object.fromEntries(requiredExports.map((name) => [name, Boolean(bybit[name])])),",
      "  manualInspectionTargets: ['submitOrder', 'cancelOrder', 'amendOrder', 'preCheckOrder', 'getInstrumentsInfo', 'getPositionInfo', 'getActiveOrders', 'getHistoricOrders', 'getExecutionList', 'getWalletBalance', 'getAccountInfo', 'getRiskLimit', 'subscribeV5', 'closeAll', 'orderLinkId', 'positionIdx', 'triggerDirection', 'closeOnTrigger', 'orderFilter', 'retCode'],",
      "};",
      "console.log(JSON.stringify(report, null, 2));",
      "if (!report.exports.RestClientV5 || !report.exports.WebsocketClient) process.exitCode = 1;"
    ],
    "requiredFollowUp": "After the script runs, manually inspect installed declarations for method signatures, category values, private subscription shape, retCode response types, order request fields, lifecycle events, and closeAll(force?) shape."
  },
  "mustVerifyInstalledSdk": {
    "clients": [
      "RestClientV5",
      "WebsocketClient"
    ],
    "privateTopics": [
      "order",
      "execution",
      "position",
      "wallet"
    ],
    "privateEvents": [
      "open",
      "response",
      "update",
      "reconnect",
      "reconnected",
      "exception"
    ],
    "restMethods": [
      "getInstrumentsInfo",
      "getPositionInfo",
      "getActiveOrders",
      "getHistoricOrders",
      "getExecutionList",
      "getWalletBalance",
      "getAccountInfo",
      "getRiskLimit",
      "submitOrder",
      "cancelOrder",
      "amendOrder",
      "preCheckOrder"
    ],
    "declarationTargets": [
      "RestClientV5",
      "WebsocketClient",
      "CategoryV5",
      "NewOrderParamsV5",
      "OrderResultV5",
      "APIResponseV3WithTime",
      "WSOrderEventV5",
      "WSExecutionEventV5",
      "WSPositionEventV5",
      "WSWalletEventV5"
    ],
    "fields": [
      "orderLinkId",
      "positionIdx",
      "triggerDirection",
      "triggerBy",
      "orderFilter",
      "closeOnTrigger",
      "reduceOnly",
      "retCode",
      "retMsg"
    ]
  },
  "requestBuilders": {
    "linearDcaLimit": {
      "clientIdField": "orderLinkId",
      "requiredFields": [
        "category",
        "symbol",
        "side",
        "orderType",
        "qty",
        "price",
        "positionIdx"
      ],
      "optionalOmitWhenUndefined": [
        "triggerPrice",
        "triggerDirection",
        "triggerBy",
        "orderFilter"
      ],
      "notes": [
        "Do not send trigger-only fields for plain limit DCA orders."
      ]
    },
    "linearTakeProfit": {
      "clientIdField": "orderLinkId",
      "requiredFields": [
        "category",
        "symbol",
        "side",
        "orderType",
        "qty",
        "price",
        "reduceOnly",
        "positionIdx"
      ],
      "notes": [
        "Verify current reduce-only semantics for the selected category and account mode."
      ]
    },
    "linearStopLoss": {
      "clientIdField": "orderLinkId",
      "requiredFields": [
        "category",
        "symbol",
        "side",
        "orderType",
        "qty",
        "triggerPrice",
        "triggerDirection",
        "triggerBy",
        "orderFilter",
        "reduceOnly",
        "closeOnTrigger",
        "positionIdx"
      ],
      "notes": [
        "Long SL below current price uses triggerDirection=2.",
        "Short SL above current price uses triggerDirection=1.",
        "Conditional market SL omits request price by default unless current Bybit docs/types require it.",
        "Verify current quantity and close-on-trigger requirements before live use."
      ]
    }
  },
  "skeletonModules": [
    {
      "path": "src/config.ts",
      "responsibility": "EXECUTION_MODE values, Bybit endpoint config, product scope, and per-symbol DCA/TP/SL settings."
    },
    {
      "path": "src/bybit/sdkSurfaceCheck.ts",
      "responsibility": "Installed package and declaration verification report."
    },
    {
      "path": "src/bybit/clientFactory.ts",
      "responsibility": "RestClientV5/WebsocketClient construction with explicit Bybit endpoint settings separated from EXECUTION_MODE."
    },
    {
      "path": "src/bybit/restHydration.ts",
      "responsibility": "Instruments, account mode, wallet, positions, active orders, history, executions, risk limits."
    },
    {
      "path": "src/bybit/privateStreams.ts",
      "responsibility": "Private order/execution/position/wallet subscription, buffering, lifecycle events, shutdown."
    },
    {
      "path": "src/state/productWorkflow.ts",
      "responsibility": "One workflow owner, symbol-side affected-scope queue, deferred reasons, replay, planning, and one action family per pass."
    },
    {
      "path": "src/planner/positionPlanner.ts",
      "responsibility": "Manual position detection, managed-slot state, desired [order intents](https://siebly.io/reference/glossary#order-intent)."
    },
    {
      "path": "src/planner/orderComparison.ts",
      "responsibility": "Bybit desired-vs-hydrated normalization and keep/amend/cancel_place/cancel/place decisions."
    },
    {
      "path": "src/execution/bybitSubmitter.ts",
      "responsibility": "throwExceptions=true business-error classification, duplicate orderLinkId preflight, pending confirmations, recoveryRequired state."
    },
    {
      "path": "src/logging/redaction.ts",
      "responsibility": "Structured sanitized logs and secret redaction."
    }
  ],
  "gotchas": [
    "Do not copy Binance newClientOrderId, clientAlgoId, positionSide, closePosition, USD-M Algo methods, or private-stream helper names into Bybit code.",
    "Prefer throwExceptions=true for order REST clients; if disabled, a resolved REST promise with retCode !== 0 is not success.",
    "Missing triggerDirection on a Bybit conditional SL can reject only the SL while later TP/DCA intents remain possible. Stop later non-sent intents after deterministic rejection, block/surface the failed slot, and reconcile any concurrently sent protective sibling.",
    "Hydrated active orders include explicit defaults that can look different from desired request objects. Normalize by order kind before replacement.",
    "Position mode and account identity should be inferred from observed accountstate when clear. Config mismatch is a review signal, not an automatic block, unless evidence remains unresolved or contradictory.",
    "Saved context without trusted active-order evidence remains unresolved, and active app-owned orders without saved context refuse mutation unless explicit adoption is configured and tested.",
    "Terminal order rows and filled DCA steps are not active protective confirmations. Completed DCA step identity must survive restart.",
    "Filter-only recovery does not prune order contexts; open-order recovery may prune only with explicit active-order evidence.",
    "The word replace should not mean cancel/place by default. Prefer amend for same-slot quantity, price, trigger price, or trigger source updates.",
    "Use scoped REST hydration on startup, reconnect, and recovery boundaries. Normal private bursts should plan from trusted accountstate after a short bounded wait for related events.",
    "A thrown Bybit business/API error or manually classified non-zero retCode is recoveryRequired with operatorPaused=false unless the operator explicitly stopped the product.",
    "Account-level WebSocket idleness is normal. Pause on lifecycle exceptions, reconnects, failed subscribe responses, rejected write-mode calls, or conflicting state, not on quiet traffic alone.",
    "Bybit demo trading and testnet are separate environment choices. Do not enable both by accident."
  ],
  "finalAcceptanceGate": [
    "README SDK surface notes reference Bybit surfaces, not Binance surfaces.",
    "Critical [behavior-contract fixtures](https://siebly.io/reference/glossary#behavior-contract-fixture) pass locally before EXECUTION_MODE=DEMO, TESTNET, or LIVE submission can be enabled.",
    "Identity mismatch/recovery fixtures cover stale, missing, duplicated, contradictory, late, and recovered states across durable context, active orders, positionIdx, private events, completed DCA steps, and recovery scopes.",
    "The dry-run golden runtime trace shows startup hydration, private topic readiness, protective SL/TP before DCA, trusted accountstate reads between action families, and final intentCount=0.",
    "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.",
    "EXECUTION_MODE is documented as the only execution-mode switch, with allowed values PUBLIC, READ_ONLY_PRIVATE, DRY_RUN_PRIVATE, DEMO, TESTNET, LIVE. Tests or fixtures prove PUBLIC and READ_ONLY_PRIVATE cannot build or submit exchange writes, DRY_RUN_PRIVATE builds the final request objects without calling exchange mutation endpoints, DEMO and TESTNET refuse unsupported exchange venues and can write only to the selected non-production venue, and LIVE is the only mode that can call production exchange mutation endpoints. README also documents Bybit execution routing, including EXECUTION_MODE=DEMO as demoTrading=true/testnet=false and EXECUTION_MODE=TESTNET as Bybit testnet routing.",
    "Private stream and workflow scheduling tests prove replan reason logging stays bounded.",
    "The DEMO, TESTNET, and LIVE execution paths are implemented for the selected Bybit product where supported, but those modes remain disabled until explicit user configuration and reviewed code."
  ]
}
