{
  "name": "Delta-V Agent Playbook",
  "version": 3,
  "baseUrl": "https://delta-v.tre.systems",
  "description": "Machine-readable guidance for autonomous Delta-V agents.",
  "preferredTransport": "mcp",
  "minimalTurnLoop": [
    "Preferred MCP path: call delta_v_quick_match (compat alias: delta_v_quick_match_connect) and keep the returned matchToken/sessionId.",
    "Call delta_v_wait_for_turn to block until your seat is actionable.",
    "If state.phase is fleetBuilding, submit fleetReady once per seat; later phases are sequential by activePlayer.",
    "Pick candidates[recommendedIndex] or build one custom legal C2S action.",
    "Call delta_v_send_action with waitForResult=true; if autoSkipLikely=true, go back to delta_v_wait_for_turn instead of chaining nextPhase.",
    "When state.phase=gameOver or outcome is present, stop the loop and call delta_v_close_session.",
    "Raw protocol fallback: POST /quick-match, poll GET /quick-match/{ticket}, connect /ws/{code}?playerToken={playerToken}, and respect phase + activePlayer yourself."
  ],
  "runtimeDecisionGuide": [
    {
      "if": "observation.state.phase == 'fleetBuilding'",
      "then": "Send fleetReady explicitly, even if purchases is empty."
    },
    {
      "if": "actionResult.autoSkipLikely == true",
      "then": "Call delta_v_wait_for_turn instead of immediately chaining the returned nextPhase."
    },
    {
      "if": "s2c.type == 'actionRejected' and reason in ['staleTurn', 'stalePhase', 'wrongActivePlayer']",
      "then": "Discard the old plan, use the returned fresh state, and re-decide."
    },
    {
      "if": "local session disconnects",
      "then": "Use delta_v_list_sessions to inspect connectionStatus and then delta_v_reconnect on the same sessionId."
    },
    {
      "if": "state.phase == 'gameOver' or state.outcome != null",
      "then": "Stop sending actions and close the helper session."
    }
  ],
  "starterScripts": [
    {
      "path": "scripts/hosted-mcp-starter.py",
      "purpose": "Minimal hosted MCP bot (Python stdlib only)"
    },
    {
      "path": "scripts/quick-start-agent.sh",
      "purpose": "One-command bridge starter against the live server"
    },
    {
      "path": "scripts/quick-match-agent.ts",
      "purpose": "Live queue bot with optional post-game review"
    },
    {
      "path": "scripts/mcp-six-agent-harness.ts",
      "purpose": "Concurrent hosted MCP regression harness"
    }
  ],
  "phaseActionMap": {
    "fleetBuilding": {
      "legalC2S": ["fleetReady"],
      "simultaneous": true,
      "requiredShape": {
        "type": "fleetReady",
        "purchases": "FleetPurchase[]"
      }
    },
    "astrogation": {
      "legalC2S": ["astrogation", "surrender"],
      "simultaneous": false,
      "requiredShape": {
        "type": "astrogation",
        "orders": "AstrogationOrder[]"
      }
    },
    "ordnance": {
      "legalC2S": ["ordnance", "emplaceBase", "skipOrdnance"],
      "simultaneous": false,
      "requiredShape": {
        "ordnance": { "type": "ordnance", "launches": "OrdnanceLaunch[]" },
        "emplaceBase": {
          "type": "emplaceBase",
          "emplacements": "OrbitalBaseEmplacement[]"
        }
      }
    },
    "combat": {
      "legalC2S": [
        "beginCombat",
        "combat",
        "skipCombat"
      ],
      "simultaneous": false,
      "requiredShape": {
        "combat": { "type": "combat", "attacks": "CombatAttack[]" }
      },
      "notes": [
        "beginCombat must be sent before combat attacks when state.pendingAsteroidHazards contains your ship ids",
        "combat attacks can be included in the single `combat` action for this turn"
      ]
    },
    "logistics": {
      "legalC2S": ["logistics", "skipLogistics"],
      "simultaneous": false,
      "requiredShape": {
        "type": "logistics",
        "transfers": "TransferOrder[]"
      }
    },
    "gameOver": {
      "legalC2S": ["rematch"]
    }
  },
  "alwaysLegal": ["chat", "ping"],
  "actionExamples": {
    "fleetReady": {
      "type": "fleetReady",
      "purchases": []
    },
    "astrogation": {
      "type": "astrogation",
      "orders": [
        {
          "shipId": "p1s0",
          "burn": 2,
          "overload": null
        }
      ]
    },
    "surrender": {
      "type": "surrender",
      "shipIds": ["p1s0"]
    },
    "ordnance": {
      "type": "ordnance",
      "launches": [
        {
          "shipId": "p1s0",
          "ordnanceType": "torpedo"
        }
      ]
    },
    "skipOrdnance": {
      "type": "skipOrdnance"
    },
    "beginCombat": {
      "type": "beginCombat"
    },
    "combat": {
      "type": "combat",
      "attacks": [
        {
          "attackerIds": ["p1s0"],
          "targetId": "p0s0"
        }
      ]
    },
    "skipCombat": {
      "type": "skipCombat"
    },
    "skipLogistics": {
      "type": "skipLogistics"
    },
    "chat": {
      "type": "chat",
      "text": "o7 commander."
    }
  },
  "tacticalGuardrails": {
    "openingPlay": [
      "Avoid early blind nukes when nearest enemy distance is unknown or >2 hexes.",
      "Prefer preserving your first hull over low-probability alpha strikes.",
      "Use astrogation to improve geometry before spending high-value ordnance."
    ],
    "ordnance": [
      "Treat nukes as high-risk; only commit with clear tactical payoff.",
      "Prefer torpedoes/mines when they create lane denial or force enemy movement.",
      "If outnumbered, avoid ordnance trades that leave you at immediate combat disadvantage."
    ],
    "phaseDiscipline": [
      "Never send an action from a stale turn/phase snapshot.",
      "Re-check current state right before sending the action."
    ],
    "chat": [
      "Keep chat <= 200 chars and avoid sensitive information.",
      "Optional: acknowledge opponent messages without spamming."
    ]
  }
}
