diff --git a/CHANGELOG.md b/CHANGELOG.md index a93a4d8..c97ac4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to PRAutoBlogger will be documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project uses [Semantic Versioning](https://semver.org/). +## [0.12.1] — 2026-04-23 + +### Fixed +- **Opik UUID v7 bug** — `PRAutoBlogger_Opik_Trace_Context` was generating trace and + span IDs with `wp_generate_uuid4()` (UUID v4). Opik's REST API requires UUID v7 and + rejects v4 with HTTP 400 "Trace id must be a version 7 UUID". Added a private static + `generate_uuid7()` method using `microtime()` + `random_bytes()` and replaced both + call sites. All trace and span submissions now succeed. + ## [0.12.0] — 2026-04-23 ### Added diff --git a/includes/services/opik/class-opik-trace-context.php b/includes/services/opik/class-opik-trace-context.php index 6ccefb3..95f69c5 100644 --- a/includes/services/opik/class-opik-trace-context.php +++ b/includes/services/opik/class-opik-trace-context.php @@ -57,7 +57,7 @@ class PRAutoBlogger_Opik_Trace_Context { * @return string The trace ID (UUID). */ public function init_trace(): string { - $this->trace_id = wp_generate_uuid4(); + $this->trace_id = self::generate_uuid7(); $this->trace_start = microtime( true ); return $this->trace_id; } @@ -86,7 +86,7 @@ class PRAutoBlogger_Opik_Trace_Context { * @return string Generated span ID (UUID). */ public function start_span( array $span_data ): string { - $span_id = wp_generate_uuid4(); + $span_id = self::generate_uuid7(); $span = array( 'id' => $span_id, @@ -193,4 +193,44 @@ class PRAutoBlogger_Opik_Trace_Context { public static function teardown(): void { self::$instance = null; } + + /** + * Generate a UUID version 7 (time-ordered, random). + * + * Opik requires v7 UUIDs for trace and span IDs. WordPress only provides + * wp_generate_uuid4() so we implement v7 here. + * + * UUID v7 structure (128 bits): + * - Bits 0-47 : Unix timestamp in milliseconds (big-endian) + * - Bits 48-51 : Version nibble (0x7) + * - Bits 52-63 : rand_a (12 random bits) + * - Bits 64-65 : Variant (0b10) + * - Bits 66-127: rand_b (62 random bits) + * + * @return string UUID v7 in standard 8-4-4-4-12 hex format. + */ + private static function generate_uuid7(): string { + $ms = (int) ( microtime( true ) * 1000 ); + $ts_hex = str_pad( dechex( $ms ), 12, '0', STR_PAD_LEFT ); // 48-bit ts -> 12 hex + $rand = bin2hex( random_bytes( 10 ) ); // 80 random bits -> 20 hex + + // Group 3: version nibble '7' + 12 random bits (3 hex chars). + $g3 = '7' . substr( $rand, 0, 3 ); + + // Group 4: variant byte (top 2 bits = 10) + 2 random hex chars. + $var_byte = ( hexdec( substr( $rand, 4, 2 ) ) & 0x3f ) | 0x80; + $g4 = sprintf( '%02x', $var_byte ) . substr( $rand, 6, 2 ); + + // Group 5: 12 remaining random hex chars. + $g5 = substr( $rand, 8, 12 ); + + return sprintf( + '%s-%s-%s-%s-%s', + substr( $ts_hex, 0, 8 ), // time_high (32 bits) + substr( $ts_hex, 8, 4 ), // time_low (16 bits) + $g3, + $g4, + $g5 + ); + } } diff --git a/prautoblogger.php b/prautoblogger.php index 83944a8..0046ea8 100644 --- a/prautoblogger.php +++ b/prautoblogger.php @@ -9,7 +9,7 @@ * Plugin Name: PRAutoBlogger * Plugin URI: https://peptiderepo.com/prautoblogger * Description: Monitors social media for trending topics, generates SEO-friendly blog posts using AI, and publishes them on a daily schedule with full cost tracking and self-improvement metrics. - * Version: 0.12.0 + * Version: 0.12.1 * Requires at least: 6.0 * Requires PHP: 7.4 * Author: PeptideRepo @@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) { | Defined here so every file in the plugin can reference paths, versions, | and limits without magic strings. */ -define( 'PRAUTOBLOGGER_VERSION', '0.12.0' ); +define( 'PRAUTOBLOGGER_VERSION', '0.12.1' ); define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' ); define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ ); define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );