'POST', 'callback' => [ __CLASS__, 'post_arbitration' ], 'permission_callback' => [ __CLASS__, 'check_admin' ], ] ); register_rest_route( self::REST_NS, '/acceptance', [ 'methods' => 'POST', 'callback' => [ __CLASS__, 'post_acceptance' ], 'permission_callback' => [ __CLASS__, 'check_admin' ], ] ); register_rest_route( self::REST_NS, '/demand/(?P[a-z\-]+)/(?P[^/]+)/trace', [ 'methods' => 'GET', 'callback' => [ __CLASS__, 'get_demand_trace' ], 'permission_callback' => [ __CLASS__, 'check_admin' ], ] ); } public static function check_admin() { return current_user_can( 'manage_options' ); } public static function post_arbitration( WP_REST_Request $request ) { $params = $request->get_json_params(); if ( ! is_array( $params ) || empty( $params ) ) { $params = $request->get_params(); } $result = SA_Orch_Arbitration::write( $params ); if ( is_wp_error( $result ) ) { return new WP_REST_Response( [ 'ok' => false, 'error' => $result->get_error_code(), 'message' => $result->get_error_message(), 'data' => $result->get_error_data(), ], 400 ); } return new WP_REST_Response( [ 'ok' => true, 'data' => $result ], 201 ); } public static function post_acceptance( WP_REST_Request $request ) { $params = $request->get_json_params(); if ( ! is_array( $params ) || empty( $params ) ) { $params = $request->get_params(); } $result = SA_Orch_Acceptance::write( $params ); if ( is_wp_error( $result ) ) { return new WP_REST_Response( [ 'ok' => false, 'error' => $result->get_error_code(), 'message' => $result->get_error_message(), 'data' => $result->get_error_data(), ], 400 ); } return new WP_REST_Response( [ 'ok' => true, 'data' => $result ], 201 ); } public static function get_demand_trace( WP_REST_Request $request ) { global $wpdb; $surface = $request['surface']; $ref = $request['ref']; $p = $wpdb->prefix . SA_ORCH_DB_PREFIX; $arbs = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$p}arbitration WHERE demand_surface = %s AND demand_ref = %s ORDER BY created_at, id", $surface, $ref ) ); $links = []; if ( ! empty( $arbs ) ) { $arb_ids = array_map( function ( $a ) { return (int) $a->id; }, $arbs ); $placeholders = implode( ',', array_fill( 0, count( $arb_ids ), '%d' ) ); $links = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$p}arbitration_plan_link WHERE arbitration_id IN ($placeholders) ORDER BY created_at, id", ...$arb_ids ) ); } $events = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$p}acceptance_event WHERE demand_surface = %s AND demand_ref = %s ORDER BY observed_at, id", $surface, $ref ) ); $closure_state_basic = SA_Orch_Acceptance::basic_closure_state( $surface, $ref ); $closure_state_role_aware = SA_Orch_Acceptance::role_aware_closure_state( $surface, $ref ); $closure_authority_valid = SA_Orch_Acceptance::closure_authority_valid( $surface, $ref ); return new WP_REST_Response( [ 'ok' => true, 'demand' => [ 'surface' => $surface, 'ref' => $ref ], 'arbitrations' => $arbs, 'plan_links' => $links, 'acceptance' => $events, // Existing field (v0.1) — retained as alias of closure_state_basic for backward compatibility 'closure_state' => $closure_state_basic, // Added in v0.2 — explicit naming 'closure_state_basic' => $closure_state_basic, 'closure_state_role_aware' => $closure_state_role_aware, 'closure_authority_valid' => $closure_authority_valid, ], 200 ); } }