From 9bd843297df7f96853304c40d3d8401268b9d3f1 Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Thu, 23 Apr 2026 18:06:54 +0000 Subject: [PATCH] fix: image style suffix empty-string bypass and image cost run_id mismatch (v0.12.2) Bug 1: Image style suffix empty-string bypass - get_option() for prautoblogger_image_style_suffix returns empty string from DB instead of default - Root cause of post #675 rendering photorealistically instead of newspaper comic - Fixed by extracting private helper get_style_suffix() that treats both absent AND empty as default - Applied to all three call sites in class-image-prompt-builder.php (lines 85, 107, 272) Bug 2: Image cost logged under wrong run_id - class-image-pipeline.php created fresh cost tracker when invoked without argument - Image costs logged under different run_id than article's other stages - Cost breakdowns aggregating by run_id silently excluded image line items - Fixed by threading article worker's cost tracker through Publisher to image pipeline - Modified publisher.php and post-assembler.php to accept and pass cost_tracker - Updated article-worker.php to pass cost_tracker to publisher methods - Image costs now share same run_id as analysis, draft, review, and llm_research stages Version: 0.12.1 -> 0.12.2 CHANGELOG: Added comprehensive entry describing both fixes --- CHANGELOG.md | 8 +++++++ includes/core/class-article-worker.php | 6 ++--- includes/core/class-image-prompt-builder.php | 23 +++++++++++++++++--- includes/core/class-post-assembler.php | 11 +++++----- includes/core/class-publisher.php | 15 ++++++++----- prautoblogger.php | 4 ++-- 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c97ac4e..9fd4dfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ 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.2] — 2026-04-24 + +### Fixed +- **Image style suffix empty-string bypass** — `get_option()` for `prautoblogger_image_style_suffix` only returns the default constant when the key is absent from `wp_options`; an empty string stored in the DB returns `''`, bypassing fallback entirely. Root cause of post #675 rendering photorealistically instead of as a newspaper comic. Fixed all three call sites in `class-image-prompt-builder.php` (lines 85, 107, 272) by extracting a private helper `get_style_suffix()` that treats both absent AND empty string as "use default". + +- **Image cost logged under wrong run_id** — `class-image-pipeline.php` created a fresh cost tracker when invoked without an argument, so image costs were logged under a different `run_id` than the article's other stages. Cost breakdowns aggregating by `run_id` silently excluded the image line item. Fixed by threading the article worker's cost tracker through `PRAutoBlogger_Publisher` and `PRAutoBlogger_Post_Assembler::attach_generated_images()` to the image pipeline constructor. Image costs now share the same `run_id` as analysis, draft, review, and llm_research stages. + + ## [0.12.1] — 2026-04-23 ### Fixed diff --git a/includes/core/class-article-worker.php b/includes/core/class-article-worker.php index 23e08b3..3cae065 100644 --- a/includes/core/class-article-worker.php +++ b/includes/core/class-article-worker.php @@ -138,13 +138,13 @@ class PRAutoBlogger_Article_Worker { : $content; if ( $auto_publish ) { - $publisher->publish( $final, $idea, $review, $run_id ); + $publisher->publish( $final, $idea, $review, $run_id, $this->cost_tracker ); ++$result['published']; } else { - $publisher->save_as_draft( $final, $idea, $review, $run_id ); + $publisher->save_as_draft( $final, $idea, $review, $run_id, $this->cost_tracker ); } } else { - $publisher->save_as_draft( $content, $idea, $review, $run_id ); + $publisher->save_as_draft( $content, $idea, $review, $run_id, $this->cost_tracker ); ++$result['rejected']; PRAutoBlogger_Logger::instance()->info( 'Article rejected by editor: ' . $idea->get_topic(), diff --git a/includes/core/class-image-prompt-builder.php b/includes/core/class-image-prompt-builder.php index ebb29c6..01f36e5 100644 --- a/includes/core/class-image-prompt-builder.php +++ b/includes/core/class-image-prompt-builder.php @@ -82,7 +82,7 @@ PROMPT; $parsed = $this->rewrite_via_llm( $title, $first_para ); - $style_suffix = get_option( 'prautoblogger_image_style_suffix', PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX ); + $style_suffix = $this->get_style_suffix(); return array( 'prompt' => trim( $parsed['scene'] . ' ' . $style_suffix ), @@ -104,7 +104,7 @@ PROMPT; $parsed = $this->rewrite_via_llm( $title, $context ); - $style_suffix = get_option( 'prautoblogger_image_style_suffix', PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX ); + $style_suffix = $this->get_style_suffix(); return array( 'prompt' => trim( $parsed['scene'] . ' ' . $style_suffix ), @@ -269,7 +269,7 @@ PROMPT; */ public function build_fallback_prompt( string $title ): array { $parsed = $this->synthesize_visual_concepts_fallback( $title, '' ); - $style_suffix = get_option( 'prautoblogger_image_style_suffix', PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX ); + $style_suffix = $this->get_style_suffix(); return array( 'prompt' => trim( $parsed['scene'] . ' ' . $style_suffix ), 'caption' => $parsed['caption'], @@ -311,4 +311,21 @@ PROMPT; $override = (string) get_option( 'prautoblogger_image_prompt_instructions', '' ); return '' !== trim( $override ) ? $override : self::REWRITER_SYSTEM_PROMPT; } + + /** + * Get image style suffix with empty-string safeguard. + * + * Treats both absent and empty string as "use default" to prevent + * silent style loss if the admin settings field is saved empty. + * + * @return string The style suffix with safe fallback. + */ + private function get_style_suffix(): string { + $style_suffix = (string) get_option( 'prautoblogger_image_style_suffix', '' ); + if ( '' === trim( $style_suffix ) ) { + $style_suffix = PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX; + } + return $style_suffix; + } + } diff --git a/includes/core/class-post-assembler.php b/includes/core/class-post-assembler.php index 872600d..d2c232e 100644 --- a/includes/core/class-post-assembler.php +++ b/includes/core/class-post-assembler.php @@ -232,11 +232,12 @@ class PRAutoBlogger_Post_Assembler { * Generate and attach images to a published post. * Non-blocking — logs errors but doesn't re-throw since the post is already created. * - * @param int $post_id Post ID. - * @param PRAutoBlogger_Article_Idea $idea Article idea (contains source IDs). - * @param array $post_data Post data array (for article content). + * @param int $post_id Post ID. + * @param PRAutoBlogger_Article_Idea $idea Article idea (contains source IDs). + * @param array $post_data Post data array (for article content). + * @param ?PRAutoBlogger_Cost_Tracker $cost_tracker Optional cost tracker for image generation cost logging. */ - public static function attach_generated_images( int $post_id, PRAutoBlogger_Article_Idea $idea, array $post_data ): void { + public static function attach_generated_images( int $post_id, PRAutoBlogger_Article_Idea $idea, array $post_data, ?PRAutoBlogger_Cost_Tracker $cost_tracker = null ): void { // Fetch the original Reddit source data for Image B's source-driven prompt. // The idea's source_ids point to rows in the source_data table collected // during the pipeline's collection step. @@ -247,7 +248,7 @@ class PRAutoBlogger_Post_Assembler { // internally, immediately after each image generates. This // ensures attachment persists even if the process times out // before this method returns. - $result = ( new PRAutoBlogger_Image_Pipeline() )->generate_and_attach_images( $post_id, $post_data, $source_data ); + $result = ( new PRAutoBlogger_Image_Pipeline( null, $cost_tracker ) )->generate_and_attach_images( $post_id, $post_data, $source_data ); if ( ! empty( $result['errors'] ) ) { foreach ( $result['errors'] as $error ) { diff --git a/includes/core/class-publisher.php b/includes/core/class-publisher.php index 2efec36..a80594a 100755 --- a/includes/core/class-publisher.php +++ b/includes/core/class-publisher.php @@ -35,9 +35,10 @@ class PRAutoBlogger_Publisher { string $content, PRAutoBlogger_Article_Idea $idea, PRAutoBlogger_Editorial_Review $review, - ?string $run_id = null + ?string $run_id = null, + ?PRAutoBlogger_Cost_Tracker $cost_tracker = null ): int { - return $this->create_post( $content, $idea, $review, 'publish', $run_id ); + return $this->create_post( $content, $idea, $review, 'publish', $run_id, $cost_tracker ); } /** @@ -54,9 +55,10 @@ class PRAutoBlogger_Publisher { string $content, PRAutoBlogger_Article_Idea $idea, PRAutoBlogger_Editorial_Review $review, - ?string $run_id = null + ?string $run_id = null, + ?PRAutoBlogger_Cost_Tracker $cost_tracker = null ): int { - return $this->create_post( $content, $idea, $review, 'draft', $run_id ); + return $this->create_post( $content, $idea, $review, 'draft', $run_id, $cost_tracker ); } /** @@ -75,7 +77,8 @@ class PRAutoBlogger_Publisher { PRAutoBlogger_Article_Idea $idea, PRAutoBlogger_Editorial_Review $review, string $post_status, - ?string $run_id = null + ?string $run_id = null, + ?PRAutoBlogger_Cost_Tracker $cost_tracker = null ): int { // Clean LLM artifacts, then inject peptide hyperlinks deterministically // before the content enters WordPress. Peptide linker no-ops gracefully @@ -106,7 +109,7 @@ class PRAutoBlogger_Publisher { PRAutoBlogger_Post_Assembler::link_generation_logs( $post_id, $run_id ); if ( 'publish' === $post_status ) { - PRAutoBlogger_Post_Assembler::attach_generated_images( $post_id, $idea, $post_data ); + PRAutoBlogger_Post_Assembler::attach_generated_images( $post_id, $idea, $post_data, $cost_tracker ); } PRAutoBlogger_Logger::instance()->info( diff --git a/prautoblogger.php b/prautoblogger.php index 0046ea8..e8b82bc 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.1 + * Version: 0.12.2 * 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.1' ); +define( 'PRAUTOBLOGGER_VERSION', '0.12.2' ); define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' ); define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ ); define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );