Destroy Ship

SkillAI & models

Destroys a corporation ship for testing. Performs the full end-to-end flow: database soft-delete, pseudo-character cleanup, and ship.destroyed event emission so connected clients update in real-time.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Destroy Ship skill

About this capability

Gradient Bang is an online multiplayer universe where you explore, trade, battle, and collaborate with other players and with LLMs

What this skill tells your AI

The instructions your AI receives, as published by pipecat-ai/gradient-bang in .claude/skills/destroy-ship/SKILL.md and read by ahel’s review.

Destroys a corporation ship for testing. Performs the full end-to-end flow: database soft-delete, pseudo-character cleanup, and ship.destroyed event emission so connected clients update in real-time.

Uses the Supabase REST API and existing record_event_with_recipients RPC — no new edge functions required.

Parameters

Ask the user for:

  • ship_id: UUID of the ship to destroy (required)

Steps

1. Source environment variables

set -a && source .env.supabase && set +a

2. Look up the ship

Fetch the ship from ship_instances via the REST API. It must exist and not already be destroyed.

curl -s "${SUPABASE_URL}/rest/v1/ship_instances?ship_id=eq.<ship_id>&destroyed_at=is.null&select=ship_id,ship_type,ship_name,current_sector,owner_type,owner_character_id,owner_corporation_id" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}"

If the result is an empty array, the ship either doesn't exist or is already destroyed. Report this and stop.

Save the ship details for subsequent steps: ship_type, ship_name, current_sector, owner_type, owner_corporation_id.

3. Look up the character name

For corporation ships, the pseudo-character has character_id = ship_id. For personal ships, use owner_character_id.

curl -s "${SUPABASE_URL}/rest/v1/characters?character_id=eq.<character_id>&select=character_id,name" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}"

Save the character name as player_name for the event payload.

4. Compute event recipients

Query sector occupants and corporation members to build the recipient list.

Sector occupants — all non-destroyed ships in the same sector:

curl -s "${SUPABASE_URL}/rest/v1/ship_instances?current_sector=eq.<sector>&destroyed_at=is.null&select=owner_character_id" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}"

Corporation members (if the ship is corporation-owned):

curl -s "${SUPABASE_URL}/rest/v1/corporation_members?corporation_id=eq.<corp_id>&select=character_id" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}"

Combine all character IDs, deduplicate, and filter out nulls. Build two parallel arrays:

  • p_recipients: array of character UUIDs
  • p_reasons: array of reason strings ("sector_observer" for sector occupants, "corp_member" for corp members)

5. Emit ship.destroyed event

Call the existing record_event_with_recipients RPC. This must happen BEFORE the destruction steps so the event references valid data.

The payload shape must match what combat_finalization.ts emits (lines 383-394):

TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)

curl -s -X POST "${SUPABASE_URL}/rest/v1/rpc/record_event_with_recipients" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "p_event_type": "ship.destroyed",
    "p_direction": "event_out",
    "p_scope": "sector",
    "p_sector_id": <current_sector>,
    "p_ship_id": "<ship_id>",
    "p_actor_character_id": null,
    "p_payload": {
      "source": {
        "type": "rpc",
        "method": "ship.destroyed",
        "request_id": "admin-destroy",
        "timestamp": "<TIMESTAMP>"
      },
      "timestamp": "<TIMESTAMP>",
      "ship_id": "<ship_id>",
      "ship_type": "<ship_type>",
      "ship_name": "<ship_name>",
      "player_type": "corporation_ship",
      "player_name": "<player_name>",
      "sector": { "id": <current_sector> },
      "combat_id": "admin-destroy",
      "salvage_created": false
    },
    "p_recipients": ["<uuid1>", "<uuid2>"],
    "p_reasons": ["sector_observer", "corp_member"],
    "p_is_broadcast": false
  }'

For personal (non-corp) ships, set "player_type": "human" instead.

6. Execute destruction

Perform database cleanup in this exact order (matches combat_finalization.ts lines 435-481). Use the REST API with the service role key.

a. Unlink character's current_ship_id:

curl -s -X PATCH "${SUPABASE_URL}/rest/v1/characters?character_id=eq.<character_id>" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"current_ship_id": null}'

b. Delete pseudo-character record (corporation ships only — the pseudo-character has character_id = ship_id):

curl -s -X DELETE "${SUPABASE_URL}/rest/v1/characters?character_id=eq.<ship_id>" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}"

c. Soft-delete ship instance (set destroyed_at):

curl -s -X PATCH "${SUPABASE_URL}/rest/v1/ship_instances?ship_id=eq.<ship_id>" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"destroyed_at": "<TIMESTAMP>"}'

d. Remove from corporation_ships (corporation ships only):

curl -s -X DELETE "${SUPABASE_URL}/rest/v1/corporation_ships?ship_id=eq.<ship_id>" \
  -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \
  -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}"

7. Report the result

Show the user:

  • Ship details: ship_id, ship_type, ship_name, sector
  • Whether the ship.destroyed event was emitted (and how many recipients)
  • Database changes made (soft-delete, pseudo-character deleted, removed from corporation_ships)

Important notes

  • Works for both corporation ships and personal ships, but the primary use case is corp ships
  • Corporation ships: pseudo-character is deleted, ship removed from corporation_ships
  • Personal ships: only steps a and c apply (unlink and soft-delete)
  • The event is emitted BEFORE destruction so it references valid DB state
  • The combat_id is set to "admin-destroy" to distinguish from real combat
  • salvage_created is always false — no salvage is generated
  • Ships already destroyed (destroyed_at IS NOT NULL) are rejected in step 2

Reference files

  • Event payload shape: deployment/supabase/functions/_shared/combat_finalization.ts lines 383-394
  • Destruction order: deployment/supabase/functions/_shared/combat_finalization.ts lines 435-481
  • RPC parameters: deployment/supabase/functions/_shared/events.ts lines 80-97
  • Client handler: client/app/src/utils/combat.ts applyShipDestroyedState()

Signals

GitHub stars
419
Forks
67
Last commit
Jun 2026
Advanced
Catalog kind
skill
Gateway key
destroy-ship
Source
github.com/pipecat-ai/gradient-bang