Commit Graph

18 Commits

Author SHA1 Message Date
peptiderepo
ed8ec9425f feat(runs): state machine, idempotent resume, reaper extension, retention
Pipeline v2 Phase 1, commit 4/4. Run_Stage_State persists per-run
per-stage state keyed run_id + stage + agent_role + item_key (item_key
scopes articles because one run_id spans a whole batch; agent_role is
the Phase-2 quorum dimension, schema-only). Done stages snapshot output
and are reused on re-entry — writer stages via
Content_Generator::set_run_item, the review via its stored verdict, a
published item skipped outright — never re-run, never re-charged. Post
creation is keyed by _prautoblogger_run_id + new _prautoblogger_idea_hash
(check-before-insert in Publisher) so retries cannot duplicate posts.
Run-level research/analysis checkpoints + done/failed lifecycle marks
ride Pipeline_Runner/Executor (incl. orphan-abort + budget-abort paths).

Run_Reaper rides the EXISTING #19 daily cron: stages stuck running >2x
expected stage wall-clock and runs silent >2x expected run wall-clock ->
failed (filterable), open stages reaped, queryable as 'incomplete'.
Retention: request_json + stage output payloads NULLed after R days, R
from the new Audit Payload Retention setting (default constant 14,
0 = keep forever, no literal at point of use). Audit_Writer records
editorial verdicts (run_decisions) and per-article kept sources
(run_sources) for new runs.

Under-300: status-transient/summary helpers extracted verbatim to
Pipeline_Status (Pipeline_Runner 271; removed the worker's duplicate).

Agent-Session: pipeline-v2-phase1-engineer-20260612
2026-06-11 16:22:59 +07:00
peptiderepo
0e1551be42 fix(phpcs): move ignore pragmas into file-level docblocks
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
2026-04-23 03:26:50 +00:00
peptiderepo
dc86376e8b style(phpcs): resolve 137 residual WordPress-Core violations
- 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
2026-04-23 03:23:52 +00:00
peptiderepo
f3eacd0d8d style(phpcs): autofix WordPress-Core violations via phpcbf
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.
2026-04-23 02:42:48 +00:00
peptiderepo
cc6979b085 Fix false stall detection for multi-article chained generation
The frontend status poller measured elapsed time from `started` (set once
at the beginning of the run) and declared a stall at 300s.  Three articles
take ~6 minutes total, so the poller always false-alarmed on article 3.

Now every stage broadcast writes `last_updated` into the transient, and
the stall check compares idle time since the last update — not since the
run started.  This lets N-article runs complete without false timeout as
long as each individual article finishes within 5 minutes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 03:40:05 +07:00
peptiderepo
ef40ce91d4 feat: chain per-article cron jobs to eliminate execution time ceiling
The pipeline previously ran all articles sequentially in a single PHP
process. On Hostinger, this timed out at ~120s — enough for 1 article
but not 2+. Now the pipeline generates article 1 in-process, then
queues articles 2..N as individual WP-Cron events. Each article gets
its own PHP process with its own execution time budget, so we can
generate any number of articles regardless of server time limits.

Architecture:
- Pipeline_Runner: orchestrate (collect+analyze+score) + first article,
  then serialize remaining ideas to wp_options and chain cron events
- Article_Worker (new): focused single-article worker extracted from
  the pipeline loop — content gen + editorial + publish
- Executor: new on_process_article_queue() handler for chained events;
  status poller re-schedules orphaned queue jobs automatically
- Article_Idea: added to_array() for queue serialization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 03:26:37 +07:00
peptiderepo
650d8df325 Fix false error: detect completed pipeline at 90s instead of 300s
The status endpoint waited 300 seconds before checking if the pipeline
had produced output. On Hostinger, PHP's max_execution_time kills the
cron process after ~120s. The pipeline publishes the post before images,
so the post exists but the transient never transitions to 'complete'.
The user sees "Generating…" for 5 minutes, then gives up.

Now the status endpoint proactively checks for recently-created posts
after just 90 seconds. If found, it reports success immediately. The
300-second hard-fail threshold is still there for genuinely dead runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 16:03:01 +07:00
peptiderepo
389cb4d380 chore: trim executor comment to stay within 300-line rule
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:24:34 +07:00
peptiderepo
abcc454dfe fix: increase fallback detection threshold from 180s to 300s
Pipeline with 3 articles + GPT-5 Image generation legitimately takes
3-4 minutes. The 180-second fallback was triggering before the pipeline
finished, incorrectly reporting "no content" when it was still running.
Increase to 300 seconds to give the pipeline room to complete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:23:29 +07:00
peptiderepo
41f1b13497 refactor: split class-executor.php into 3 focused classes (300-line rule)
Extract PRAutoBlogger_Generation_Lock (DB mutex) and
PRAutoBlogger_Ajax_Handlers (image/model/test AJAX) from the
482-line executor. Executor retains generation cron/AJAX + model
registry at 299 lines. Document background generation flow in
ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:08:46 +07:00
peptiderepo
4c49e8593e fix: add ignore_user_abort + fallback completion detection for cron
The WP-Cron loopback is also subject to Hostinger's 120s timeout.
Adding ignore_user_abort(true) keeps the PHP process alive after
LiteSpeed closes the connection.

Also adds fallback completion detection: if the transient is stuck
at "running" but the generation lock was released (pipeline finished),
the status endpoint now checks for recently-created posts to confirm
success and recover gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 13:59:37 +07:00
peptiderepo
b5bbc54f87 fix: move Generate Now to background processing to avoid 120s timeout
Hostinger's LiteSpeed web server kills any HTTP connection that runs
longer than 120 seconds with a 500 "Request Timeout" error. The full
pipeline (Reddit fetch → topic scoring → article generation → editorial
pass → image gen ×2) takes 120-180s, causing "Request failed: error"
on every manual generation attempt.

Architecture change:
- Generate Now AJAX handler now schedules a one-shot WP-Cron event
  and returns immediately (~100ms response time)
- New prautoblogger_manual_generation cron action runs the pipeline
  in a separate PHP process free from the connection timeout
- Pipeline progress is written to a transient at each stage
- New prautoblogger_generation_status AJAX endpoint lets the frontend
  poll progress every 5 seconds
- Frontend auto-resumes polling on page load if a generation is running
- Direct wp-cron.php loopback ensures cron fires even if DISABLE_WP_CRON
  is set (common on Hostinger)

Version bump: 0.2.9 → 0.3.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 13:52:10 +07:00
peptiderepo
1a2af595c6 fix: attach images immediately + upgrade image prompts to v2
Two fixes:

1. Image attachment: moved set_post_thumbnail() and Image B post meta
   into generate_and_attach_images() so they persist immediately after
   each image generates. Previously the caller set them after both images
   completed — if the process timed out (common with GPT-5 Image at
   ~30s/image), the featured image was never attached and Image B was
   never attempted.

2. Image prompts: replaced FLUX.1-optimized rewriter system prompt and
   90s infomercial style suffix with prompts optimized for Gemini Flash
   Image and GPT-5 Image. New style: premium scientific lifestyle
   photography with golds/teals color grading. Includes one-time migration
   to update stored style suffix.

Also logs when Image B is skipped due to missing source data (previously
silent), helping diagnose why some posts only get one image.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 12:17:05 +07:00
peptiderepo
1968864073 fix: widen all catch blocks from \Exception to \Throwable
PHP 8.3 TypeError and Error extend \Throwable but NOT \Exception,
so catch(\Exception) silently drops them — explaining why the
generation pipeline fails at the publishing step with no activity
log entry. Now every catch in the generation path catches
\Throwable and logs the exception class name for diagnostics.

Bump to v0.2.7.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 10:12:06 +07:00
peptiderepo
b5e91ba3a6 fix: wire Image B source data — was hardcoded null, never generated
Root cause: both attach_generated_images() in post-assembler and the
retroactive handler in executor passed null as source_data to the image
pipeline. The pipeline's Image B branch requires non-null source_data
to fire, so Image B was silently skipped on every article since the
feature was built.

Fix: added get_source_data_for_image() to Source_Collector to retrieve
the original Reddit post title/content by source_ids. Post-assembler
now pulls source_ids from the Article_Idea; executor pulls them from
the _prautoblogger_source_ids post meta saved by the publisher.

Also added a regression test proving Image B is skipped when source_data
is null, so this handoff gap will be caught if re-introduced.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 08:34:21 +07:00
peptiderepo
31f303a831 feat: interactive model picker with searchable popup and pricing
Replace plain text inputs for analysis/writing/editor model fields
with a rich model picker that fetches the OpenRouter model registry
and displays a searchable popup with model name, provider, input/output
pricing per 1M tokens, and context window. Also extracts source status
and model field renderers into dedicated field classes to keep
class-admin-page.php under the 300-line cap.

New files:
- assets/js/model-picker.js — popup UI with search, filtering, caching
- assets/css/model-picker.css — popup styles matching admin design system
- includes/admin/fields/class-open-router-model-field.php — server render
- includes/admin/fields/class-source-status-field.php — extracted field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 02:53:40 +07:00
peptiderepo
0e44c52ad8 feat: add AJAX endpoint for retroactive image generation
Adds wp_ajax_prautoblogger_generate_image so images can be generated
for existing posts that were published before image gen was enabled
or fixed. Uses set_time_limit(120) to handle the Cloudflare API
latency that caused fatals when run via Code Snippets init hook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 00:39:10 +07:00
peptiderepo
4d09141550 refactor: extract PRAutoBlogger_Executor from PRAutoBlogger (404→186+186)
Cron handlers (daily generation, metrics collection), AJAX handlers
(generate now, test connection), atomic generation lock, and model
registry access moved to PRAutoBlogger_Executor. Main orchestrator
retains hook registration, DB migrations, and cron schedule filter.
Backward-compatible proxy methods on PRAutoBlogger.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 02:48:16 +07:00