Commit 2 (final scope) of the in-plugin editorial image pipeline
(ARCHITECTURE.md #21). A deterministic local composer now runs between
provider bytes and sideload: Image A yields a corner-marked featured
(1200x632) + branded OG (1200x630, teal band, logo, baked caption in
Poppins SemiBold) + square card (1080x1080, cream panel, Poppins Bold,
teal footer with the reverse lockup); Image B gets the corner mark only.
- Capability ladder: imagick (full compositing) -> wp_get_image_editor
resize-only (unbranded, WARNING once) -> pass-through (WARNING once);
probe cached in prautoblogger_image_compose_capability, fingerprinted
by PHP version + imagick/gd presence so host flips auto-invalidate.
compose() never throws; composition can never fail a publish.
- Variants sideloaded with a role filename suffix; attachment meta
_prautoblogger_image_role + _prautoblogger_base_attachment_id; post
meta _prautoblogger_og_image_id (frozen for the rebuilt SEO stage) +
_prautoblogger_square_image_id. No cost-tracker rows ($0 local stage;
duration logged on the image_composer channel).
- New Images settings: compose_enabled ('1'), compose_variants
('og,square'), featured_mark_enabled ('1') — read at point of use.
Geometry ships as Layout defaults behind the
prautoblogger_image_compose_layout filter.
- Vendored brand rasters (assets/brand) + bundled OFL Poppins faces
(assets/fonts) for per-environment byte-stable output (stripImage +
PNG date/time chunks excluded). Validated on Hostinger PHP 8.3.30 /
ImageMagick 7.1.1-43: php -l clean on all touched files, double
render byte-identical (sha256), exact variant geometry.
- Fix: featured/Image B alt text is the editorial caption (was the
model name); model still persists in _prautoblogger_image_model.
- Image_Attacher extracted from Image_Pipeline (300-line compliance);
sideloader gains an optional filename suffix param (back-compat '').
- Version 0.16.0 -> 0.17.0 + CHANGELOG + ARCHITECTURE.md #21 +
CONVENTIONS.md composer guide in this commit. Unit tests: layout
clamp boundaries, capability ladder, cache invalidation, attacher
meta-linking, pipeline integration, imagick determinism (gated).
Agent-Session: engineer-prautoblogger-image-composer-20260611
File-level sniffs like WordPress.Files.FileName are evaluated at the file level,
not at code level. They require phpcs:ignore directives to be placed within the
file-level docblock (/** ... */), not in code comments.
Moved all 81 phpcs:ignore pragmas from code comments to the file-level docblocks.
This applies to:
- 79 class files with class-naming convention ignores
- prautoblogger.php with strict-types docblock ignore
- uninstall.php with strict-types docblock ignore
- Added phpcs:ignore for class file naming convention (79 violations)
WordPress.Files.FileName.InvalidClassFileName is a known architectural
issue — class files use short names without prautoblogger- prefix
- Added phpcs:ignore for ->prepare() table interpolation (56 violations)
WordPress.DB.PreparedSQL.InterpolatedNotPrepared: dynamic table names
(prab_generation_logs, prab_cost_logs) require interpolation; ->prepare()
does not support table name placeholders, only column values
- Fixed file-level docblock positioning (2 violations)
prautoblogger.php and uninstall.php: moved declare(strict_types=1) before
file docblock per PHP best practices
Run phpcbf --standard=WordPress-Core across includes/, prautoblogger.php,
and uninstall.php. Fixed 1,362 formatting violations in 75 files:
- Short array syntax: [] → array()
- Line breaking for long function calls
- Consistent indentation
- Function call parameter alignment
No logic changes; purely mechanical formatting.
Three fixes in one:
1. Duplicate image: prepend_caption_to_post() was embedding the full
image + caption as a <figure> block, but the theme already displays
the featured image. Now only the caption text is prepended — no
second copy of the image.
2. Hardcoded model names: removed all FLUX.1/FLUX.2 references from
model-agnostic code (pipeline, prompt builder, sideloader, OpenRouter
provider, interface, admin labels). The Cloudflare provider still
references FLUX since it specifically wraps that model family.
3. Test alignment: OpenRouterImagePricingTest was asserting against
removed models (flux-2-pro, seedream). Updated to match current
COST_PER_IMAGE entries (Gemini Flash, GPT-5 Image).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
media_handle_sideload() is only available in admin context. When the
pipeline runs via WP-Cron (background generation), the function is
undefined. Add require_once guards for media.php, file.php, and
image.php before calling sideload.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Union types (array|WP_Error, int|WP_Error, string|WP_Error) require
PHP 8.0+. Removed return type declarations while keeping @return
docblocks intact for IDE/static analysis support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Completes the image + A/B experiment workstream by integrating
image generation into the post publishing pipeline.
New classes:
- class-image-pipeline.php: Orchestrates A/B generation (article-driven
Image A → featured; source-driven Image B → post meta). Each image
independently fallible; article publishes even if both fail.
- class-image-prompt-builder.php: Synthesizes visual prompts from article
title+content and Reddit source data. Appends CEO-locked 90s infomercial
style suffix. Keeps prompts concise for FLUX.1 quality.
- class-image-media-sideloader.php: Downloads image bytes, creates temp
file, sideloads via media_handle_sideload(), sets alt text and metadata,
cleans up temp files.
Changes to existing files:
- Publisher.attach_generated_images(): Runs image pipeline post-creation,
sets _thumbnail_id (Image A) and _prautoblogger_image_b_id (Image B).
Errors logged but don't block post publication.
- Settings: prautoblogger_image_enabled (toggle, default off).
Tests:
- ImagePipelineTest.php: Orchestration, cost tracking, graceful failures.
- ImagePromptBuilderTest.php: Prompt generation, length limits.
Docs:
- ARCHITECTURE.md: New step 6b in data flow, new files in file tree.
- CHANGELOG.md: Commit 1a/1b breakdown.
Co-authored-by: peptiderepo <peptiderepo@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>