feat: deterministic image composer — OG + square variants, branding, baked caption (v0.17.0)

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
This commit is contained in:
peptiderepo
2026-06-11 12:31:03 +07:00
parent d559fc58ab
commit 28947a0caa
26 changed files with 2416 additions and 128 deletions

View File

@@ -150,6 +150,38 @@ interface PRAutoBlogger_Image_Provider_Interface {
---
## How To: Tune the Image Composer (v0.17.0+)
The deterministic composer (ARCHITECTURE.md #21) renders branded variants between
provider bytes and sideload. Three rules when touching it:
1. **Geometry lives in code, not settings.** All sizes/colors/opacities come from
`PRAutoBlogger_Image_Composer_Layout::defaults()` and can be overridden via the
`prautoblogger_image_compose_layout` filter:
```php
add_filter( 'prautoblogger_image_compose_layout', function ( array $layout ): array {
$layout['featured']['mark_opacity'] = 0.40;
return $layout;
} );
```
Promote a value to a settings field only on explicit CEO request.
2. **Keep renderer classes WP-free.** `Image_Composer_Imagick`, `Image_Composer_Canvas`,
and `Image_Composer_Layout` must not call WordPress functions — they are exercised by
a standalone render harness (and the determinism test) without a WP bootstrap. Only
the orchestrator (`Image_Composer`) and the GD rung (`Image_Composer_Editor`) may
touch WP APIs.
3. **Never fatal a publish.** Any new render path needs a try/catch that degrades to
pass-through and logs at most one WARNING per run (`warn_once()`). Capability checks
go through the cached probe (`prautoblogger_image_compose_capability` option +
same-named override filter) — never re-probe per image.
Adding a new variant role: extend `Layout::defaults()` + the renderer, whitelist the
role in `Image_Composer::configured_variants()` and in
`Image_Attacher::ROLE_POST_META` (new `_prautoblogger_{role}_image_id` post meta keeps
the uninstall prefix-sweep effective), then cover it in the unit ladder tests.
---
## How To: Add a New Admin Setting
1. Define the option key in the settings array in `admin/class-admin-page.php` → `get_settings_fields()` method