{
  "apply_enabled_now": false,
  "current_db_summary": {
    "active_leases": 1,
    "enabled_peers": 5,
    "forced_leases": 0,
    "idle_leases": 4,
    "lease_rows": 5,
    "pending_jobs": 0
  },
  "example_action": {
    "action": "reassign_idle_peer",
    "active_peer_move": false,
    "forced": false,
    "from_egress_class": "cs3",
    "from_target_id": "cs3_vpn5",
    "peer_id_source": "resolve_from_peer_egress_leases_by_tunnel_ip_for_update",
    "simulation_only": true,
    "state": "idle",
    "to_egress_class": "cs2",
    "to_target_id": "cs2_vpn4",
    "tunnel_ip": "10.253.1.12",
    "would_create_job": true,
    "would_write_db": true
  },
  "mode": "design-readonly-no-apply",
  "observed_indexes": [
    {
      "indexdef": "CREATE UNIQUE INDEX egress_allocator_settings_pkey ON public.egress_allocator_settings USING btree (key)",
      "indexname": "egress_allocator_settings_pkey",
      "schemaname": "public",
      "tablename": "egress_allocator_settings"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX egress_targets_egress_class_key ON public.egress_targets USING btree (egress_class)",
      "indexname": "egress_targets_egress_class_key",
      "schemaname": "public",
      "tablename": "egress_targets"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX egress_targets_pkey ON public.egress_targets USING btree (id)",
      "indexname": "egress_targets_pkey",
      "schemaname": "public",
      "tablename": "egress_targets"
    },
    {
      "indexdef": "CREATE INDEX peer_egress_leases_class_state_idx ON public.peer_egress_leases USING btree (egress_class, state)",
      "indexname": "peer_egress_leases_class_state_idx",
      "schemaname": "public",
      "tablename": "peer_egress_leases"
    },
    {
      "indexdef": "CREATE INDEX peer_egress_leases_forced_idx ON public.peer_egress_leases USING btree (forced)",
      "indexname": "peer_egress_leases_forced_idx",
      "schemaname": "public",
      "tablename": "peer_egress_leases"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX peer_egress_leases_peer_id_uq ON public.peer_egress_leases USING btree (peer_id)",
      "indexname": "peer_egress_leases_peer_id_uq",
      "schemaname": "public",
      "tablename": "peer_egress_leases"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX peer_egress_leases_pkey ON public.peer_egress_leases USING btree (id)",
      "indexname": "peer_egress_leases_pkey",
      "schemaname": "public",
      "tablename": "peer_egress_leases"
    },
    {
      "indexdef": "CREATE INDEX peer_egress_leases_target_id_idx ON public.peer_egress_leases USING btree (target_id)",
      "indexname": "peer_egress_leases_target_id_idx",
      "schemaname": "public",
      "tablename": "peer_egress_leases"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX peer_egress_leases_tunnel_ip_uq ON public.peer_egress_leases USING btree (tunnel_ip)",
      "indexname": "peer_egress_leases_tunnel_ip_uq",
      "schemaname": "public",
      "tablename": "peer_egress_leases"
    },
    {
      "indexdef": "CREATE INDEX peer_egress_leases_updated_at_idx ON public.peer_egress_leases USING btree (updated_at)",
      "indexname": "peer_egress_leases_updated_at_idx",
      "schemaname": "public",
      "tablename": "peer_egress_leases"
    },
    {
      "indexdef": "CREATE INDEX ix_peers_enabled ON public.peers USING btree (enabled)",
      "indexname": "ix_peers_enabled",
      "schemaname": "public",
      "tablename": "peers"
    },
    {
      "indexdef": "CREATE INDEX ix_peers_node_id ON public.peers USING btree (node_id)",
      "indexname": "ix_peers_node_id",
      "schemaname": "public",
      "tablename": "peers"
    },
    {
      "indexdef": "CREATE INDEX ix_peers_user_id ON public.peers USING btree (user_id)",
      "indexname": "ix_peers_user_id",
      "schemaname": "public",
      "tablename": "peers"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX peers_pkey ON public.peers USING btree (id)",
      "indexname": "peers_pkey",
      "schemaname": "public",
      "tablename": "peers"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX uq_peers_node_tunnel_ip_enabled ON public.peers USING btree (node_id, tunnel_ip) WHERE (enabled = true)",
      "indexname": "uq_peers_node_tunnel_ip_enabled",
      "schemaname": "public",
      "tablename": "peers"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX uq_peers_public_key ON public.peers USING btree (public_key)",
      "indexname": "uq_peers_public_key",
      "schemaname": "public",
      "tablename": "peers"
    },
    {
      "indexdef": "CREATE INDEX ix_provisioning_jobs_node_id ON public.provisioning_jobs USING btree (node_id)",
      "indexname": "ix_provisioning_jobs_node_id",
      "schemaname": "public",
      "tablename": "provisioning_jobs"
    },
    {
      "indexdef": "CREATE INDEX ix_provisioning_jobs_peer_id ON public.provisioning_jobs USING btree (peer_id)",
      "indexname": "ix_provisioning_jobs_peer_id",
      "schemaname": "public",
      "tablename": "provisioning_jobs"
    },
    {
      "indexdef": "CREATE INDEX ix_provisioning_jobs_status ON public.provisioning_jobs USING btree (status)",
      "indexname": "ix_provisioning_jobs_status",
      "schemaname": "public",
      "tablename": "provisioning_jobs"
    },
    {
      "indexdef": "CREATE UNIQUE INDEX provisioning_jobs_pkey ON public.provisioning_jobs USING btree (id)",
      "indexname": "provisioning_jobs_pkey",
      "schemaname": "public",
      "tablename": "provisioning_jobs"
    }
  ],
  "policy_invariants": [
    "allocator_enabled must be 1",
    "force_mode must be off",
    "rebalance_paused must be 0",
    "pending provisioning jobs must be 0",
    "only idle non-forced leases may be reassigned",
    "active leases are pinned and must not be moved",
    "forced leases are pinned and must not be moved",
    "current target_id/state/forced values must be rechecked inside the transaction before update",
    "last_rx/tx baselines and last_activity_at must be preserved during egress reassignment",
    "cooldown_until should be set to now + ROUTER_EGRESS_REASSIGN_COOLDOWN_SECONDS",
    "selector/runtime changes must be delegated through provisioning job/agent automation, not manual systemctl start"
  ],
  "required_schema": {
    "egress_allocator_settings": {
      "key": true,
      "value_int": true,
      "value_text": true
    },
    "egress_targets": {
      "egress_class": true,
      "id": true,
      "interface_name": true
    },
    "peer_egress_leases": {
      "assignment_source": true,
      "cooldown_until": true,
      "egress_class": true,
      "forced": true,
      "last_activity_at": true,
      "last_rx_bytes": true,
      "last_rx_packets": true,
      "last_tx_bytes": true,
      "last_tx_packets": true,
      "peer_id": true,
      "reason": true,
      "state": true,
      "target_id": true,
      "tunnel_ip": true,
      "updated_at": true
    },
    "peers": {
      "enabled": true,
      "id": true,
      "tunnel_ip": true
    },
    "provisioning_jobs_useful_columns": {
      "created_at": true,
      "payload_json": true,
      "status": true,
      "updated_at": false
    }
  },
  "required_schema_all_ok": true,
  "rollback_contract": {
    "required": true,
    "strategy": [
      "before apply, save exact old rows for all changed peer_egress_leases",
      "save created provisioning_job ids",
      "rollback SQL restores old target_id, egress_class, assignment_source, reason, cooldown_until, updated_at where peer_id matches",
      "rollback marks/deletes only jobs created by this run, never unrelated jobs"
    ]
  },
  "safety": {
    "apply_still_disabled": true,
    "db_written": false,
    "jobs_created": false,
    "selector_changed": false,
    "wg_runtime_changed": false
  },
  "schema": "wgpaid-rebalancer-apply-contract-v1",
  "source_live_plan": {
    "db_summary": {
      "active_leases": 1,
      "enabled_peers": 5,
      "forced_leases": 0,
      "idle_leases": 4,
      "lease_rows": 5,
      "pending_jobs": 0
    },
    "mode": "dry-run",
    "settings": {
      "allocator_enabled": "1",
      "force_mode": "off",
      "rebalance_paused": "0"
    },
    "summary": {
      "action_count": 0,
      "active_count": 1,
      "blocked": false,
      "forced_count": 0,
      "idle_count": 4,
      "lease_rows": 5,
      "noop": true,
      "selector_match_count": 5,
      "slot_seen_count": 5,
      "target_count": 5
    }
  },
  "sql_templates": {
    "job_insert_strategy": "Use current provisioning_jobs schema; payload_json is required. Do not implement job insert until status/type column contract is confirmed in the next implementation step.",
    "job_payload_json": {
      "from_egress_class": "$old_egress_class",
      "from_target_id": "$old_target_id",
      "peer_id": "$peer_id",
      "reason": "rebalancer_idle_reassign",
      "requested_by": "wgpaid-egress-rebalancer-once",
      "to_egress_class": "$new_egress_class",
      "to_target_id": "$new_target_id",
      "tunnel_ip": "$tunnel_ip",
      "type": "peer_egress_reassign"
    },
    "lock_candidate": "SELECT * FROM public.peer_egress_leases WHERE tunnel_ip = $1::inet FOR UPDATE;",
    "update_lease": "UPDATE public.peer_egress_leases SET target_id=$new_target_id, egress_class=$new_egress_class, assignment_source='rebalancer', reason='rebalancer_idle_reassign', cooldown_until=now()+make_interval(secs=>$cooldown_seconds), updated_at=now() WHERE peer_id=$peer_id AND state='idle' AND forced=false AND target_id=$old_target_id;"
  },
  "transaction_contract": {
    "isolation": "default_read_committed_with_row_locks",
    "steps": [
      "BEGIN",
      "read allocator settings FOR SHARE-equivalent",
      "abort if allocator disabled, force_mode != off, rebalance_paused != 0, or pending jobs exist",
      "lock selected peer_egress_leases rows FOR UPDATE",
      "for each action: verify row is still idle, forced=false, current target_id matches dry-run input",
      "update peer_egress_leases target_id, egress_class, assignment_source, reason, cooldown_until, updated_at",
      "insert provisioning job carrying old/new target/class/tunnel_ip in payload_json",
      "COMMIT",
      "after commit: write backend/agent trigger event if required by current automation"
    ]
  }
}
