- class-image-composer.php: swap the fingerprint comparison operands
(WordPress.PHP.YodaConditions flagged the parenthesized null-coalesce
expression on the right-hand side).
- ImageComposerTest: the two capability-cache tests asserted
update_option calls via Functions\expect(), which the setUp
Functions\when() stub shadows (Mockery dispatches the call to the
first matching expectation), so the with()-matcher saw 0 calls once
the CI runner's real imagick made the probe path live. Capture calls
with a when() alias instead (the suite's house pattern) and assert on
the captured writes - including autoload=false and the rewritten
fingerprint. The matching-fingerprint test now genuinely asserts
zero writes instead of a trivially-satisfied never().
No composer behavior change beyond the operand swap.
Agent-Session: engineer-prautoblogger-image-composer-20260611
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
Replaces the per-repo staging/smoke/production jobs with one call to
peptiderepo/peptide-e2e/.github/workflows/deploy-app.yml (target_path=wp-content/plugins/prautoblogger).
Validated end-to-end on the calculator pilot.
Agent-Session: cto rollout-reusable-deploy
listPullRequestsAssociatedWithCommit is eventually-consistent after a squash
merge; the single immediate call intermittently flagged legit merges as
'direct push to main'. Now polls up to 5x/10s before opening an issue.
Agent-Session: cto audit-race-fix
* Commit 1: editorial image prompt + style-template setting
Pivot the image pipeline from the single-panel comic style to a text-free
editorial scientific illustration, and replace the Style Suffix setting with
an editable Style Template setting. Commit 1 of the in-plugin editorial image
pipeline (no composer yet; pipeline otherwise unchanged).
- New constant PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_TEMPLATE (brief §7 editorial
template with the single {{ topic_summary }} token).
- New setting prautoblogger_image_style_template (textarea) replaces the
Style Suffix field; single-token validation on save (brief A5); old
prautoblogger_image_style_suffix mirrored to _deprecated for one cycle and
not deleted (one-time migration prautoblogger_migrated_style_template_v0160).
- REWRITER_SYSTEM_PROMPT now emits a 1-2 sentence editorial topic/mechanism
summary as the SCENE (no comic gag), keeping the CAPTION line for the
unchanged HTML-caption-below-image path; scene/caption parsing contract
preserved.
- New PRAutoBlogger_Image_Template_Filler substitutes the scene into the
template's {{ topic_summary }} slot across all three entry points; strips
control chars + clamps length (A5); degrades safely on missing token or
empty summary so a blank/token-only prompt is never sent to the provider
(A5/A6).
- Version bump 0.15.1 -> 0.16.0 + CHANGELOG + ARCHITECTURE.md updates.
Agent-Session: image-editorial-commit1
* style: auto-fix PHPCS violations via phpcbf
---------
Co-authored-by: peptiderepo <peptiderepo@users.noreply.github.com>
- cto-review.yml: pass the PR diff to the review step via a file
(/tmp/cto-review-diff.txt) instead of a step output/env var. A ~400KB diff
in the process env exceeds the runner ARG_MAX and failed the 'Call 4 models'
step with 'Argument list too long' (#110).
- deploy.yml: remove invalid 'continue-on-error: true' from the smoke job
(a reusable-workflow 'uses:' call). Actions does not support this property
on 'uses:' jobs; it produced a 0-job workflow failure that has been failing
every deploy. Matches the fix already in prcore/theme.
Closes#110.
Agent-Session: cto fix-cto-review-and-deploy
Co-authored-by: peptiderepo <peptiderepo@users.noreply.github.com>
Three bugs in test_sync_success_caches_and_returns_models:
1. Mock data lacked taskType=imageInference so normalize_models() filtered
everything out → sync() returned false (empty result).
2. wp_remote_retrieve_response_code and wp_remote_retrieve_body not mocked
→ Brain\Monkey MissingFunctionExpectations.
3. is_wp_error not mocked.
4. get_option/update_option used separate static stores so update_option
write was invisible to the subsequent get_option in get_models().
RunwareModelCatalogTest mocks time() via Patchwork/Brain\Monkey. Without
declaring it in redefinable-internals, Patchwork throws NotUserDefined
on every test in that suite (18 failures).
get_models() now delegates to PRAutoBlogger_Runware_Model_Catalog which
calls get_option() to check WP cache. Brain\Monkey threw MissingFunction
Expectations on all registry tests. Added setUp() stub: get_option returns
false (no cache) so catalog falls back to hardcoded list; update_option
stubbed to prevent side-effect errors.
Orphaned array entry for runware_catalog_sync field was placed after the
closing ); of the fields array instead of inside it. PHP parse error on
line 268. Moved entry to correct position inside the array.
Replace hardcoded model registry with live-sync system.
New PRAutoBlogger_Runware_Model_Catalog class fetches the Runware model
catalog daily, caches in WP options (24h TTL), and falls back to the
hardcoded 15-model list if the API is unavailable. Sync Now button
in PRAdmin for on-demand refresh. Eliminates the drift that caused
silent model fallback bugs (v0.13.8, v0.14.0).
All Runware model selections except schnell and dev were silently falling
back to runware:100@1 because COST_PER_IMAGE only had 2 entries while the
image model registry was expanded to 15 models in v0.13.8.
Fix: add all 15 model IDs to COST_PER_IMAGE and DEFAULT_STEPS.
applySort() always read input_price_per_m for price sorting, but image
models carry cost_per_image instead — input_price_per_m is undefined on
them, so every image model got an effective price of 0 and the sort was a
no-op.
Fix: detect image models via cost_per_image !== undefined and use that
field when present; fall back to input_price_per_m for text models.
Files changed:
- assets/js/model-picker.js — applySort price branch
- prautoblogger.php — version bump 0.13.8 → 0.13.9
- CHANGELOG.md — release notes
35% title + 255px fixed PRAB cols left ~50px per auto column.
Replace with a balanced budget across all columns so Author,
Categories, Tags, and Date all get enough room to render horizontally:
title 22%, author 6%, categories 7%, tags 9%, date 8%,
writing_model/image_model 85px each, cost 50px, peptide_topics 75px
table-layout: fixed ignores min-width — the WP posts list table uses
fixed layout so min-width: 80px had no effect. Changed to width: 80px
which is honoured under fixed-layout tables.
Two bugs in class-post-list-columns.php:
1. Cost always showed $0.00: a PHP '// phpcs:ignore' comment was
placed inside the SQL string literal, causing MariaDB syntax error
on every render_cost_cell() query. Moved comment to the line above
wpdb->prepare() where it belongs.
2. Peptide Topics column header compressed to near-zero width by the
three fixed-width PRAB columns (Writing Model + Image Model + Cost =
255px). Added min-width: 80px to .column-taxonomy-peptide_topic so
the header renders horizontally.
Root cause: .ab-mp-price and .ab-mp-ctx were siblings in .ab-mp-row-meta
with price first. Description text is variable-length, so the meta div
grew/shrank per row, shifting the price left/right.
Fix:
- Move price to last child in .ab-mp-row-meta so its right edge is
always pinned to the row's fixed right edge
- Add flex-shrink: 0 to .ab-mp-price so it never compresses
- Cap .ab-mp-ctx at max-width 200px with text-overflow: ellipsis
- Swap column header order (Context, then Price) to match new DOM order
.ab-mp-col-price was missing min-width and right-alignment even though
.ab-mp-price had both. The header sat left while values sat right,
so they didn't track the same column edge. Added min-width: 90px and
justify-content: flex-end to match.
- Add .ab-mp-col-headers bar between search and list with three
clickable column headers: Model (sorts by name), Price in/out
(sorts by input_price_per_m), Context (sorts by context_length)
- Track sortState at module level so preference persists across
open/close; clicking same column toggles asc/desc
- Deprecated models always sink to bottom regardless of sort column
- Add CSS for .ab-mp-col-headers, .ab-mp-col-header, .ab-mp-sort-arrow,
.ab-mp-col-active; widths mirror existing row flex layout so columns
visually align with data
Bumps version 0.13.1 → 0.13.2
Add min-width: 90px and text-align: right to .ab-mp-price so prices
like $0.0006/image and $0.0800/image stack on a consistent right edge
instead of floating at uneven offsets.
class-autoloader.php listed services/ but not services/opik/. All four Opik classes live in that subdirectory. Composer classmap covered it (tests passed); WP runtime autoloader would fatal in production on first Opik class reference. Adds services/opik/ to the scan list. Bumps to v0.12.3.
Opik requires version 7 UUIDs. wp_generate_uuid4() was producing v4
which the API rejected with 422. Replaced both call sites in
class-opik-trace-context.php with generate_uuid7(), a private static
method implementing RFC 9562 §5.7.
Fixes: smoke test 422 {"errors":["Trace id must be a version 7 UUID"]}
Dynamic table names ({}, {}) are used in
database queries and cannot be parameterized with $wpdb->prepare() which only
supports value placeholders, not table-name placeholders.
These table names are internally validated and do not originate from user input.
Excluding WordPress.DB.PreparedSQL.InterpolatedNotPrepared from the ruleset.
Created phpcs.xml ruleset that inherits from WordPress-Core but excludes
WordPress.Files.FileName.InvalidClassFileName sniff. This is an architectural
choice: PRAutoBlogger uses short class names (class-logger.php) instead of
fully-qualified names (class-prautoblogger-logger.php) for better readability.
Updated CI workflow to use phpcs.xml instead of --standard=WordPress-Core.
Remaining violations (56 wpdb + 2 docblock) are still addressed with inline
phpcs:ignore pragmas with documented reasons.
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
- PRAUTOBLOGGER_VERSION constant now matches header (0.10.1)
- CHANGELOG.md updated to accurately reflect Round 3 work:
* OpenRouter test encryption fix (real class round-trip)
* 137 PHPCS violations resolved with targeted ignores + reasons
* Clear accounting of which categories of violations and why ignored
- 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
Adds missing wp_salt() and wp_generate_uuid4() stubs to BaseTestCase::setUp()
so tests that call PRAutoBlogger_Encryption (for API key decryption) don't fail.
Fixes PublisherTest::test_publish_links_generation_logs_by_run_id() which was
capturing the wrong database query (SELECT from SourceCollector) due to test
fixture including source_ids. Now creates a fixture override without source_ids
to isolate the run_id linking assertion.
Related: Failures #1 and #2 from PR #109.
Bump version: 0.10.0 → 0.10.1
Updated CHANGELOG.md with Fixed/Changed entries:
- Missing post_type_exists() stub in BaseTestCase
- PHPCS autofix of 1,362 violations across 75 files
- CI gate flipped to strict mode
Updated ARCHITECTURE.md with ADR #20 documenting the test infra
and PHPCS compliance improvements.
Change continue-on-error from true to false for the PHPCS check.
Now that all 1,362 violations are fixed via phpcbf, PHPCS failures
will block CI. This prevents regression of style standards.