CEO ask #5 complete: browsable paginated run list + full input/output inspection
per pipeline stage.
## What was built
### Generations list (prautoblogger-gen-history)
Hidden admin page (options.php-parent, priority 14) showing all pipeline runs newest
first, 20 per page. Each row: article title (linked to dossier when a post exists),
start date, status chip, distinct model(s), settled cost, duration. Queries bounded
(LIMIT/OFFSET) — no unbounded SELECT *.
New files:
- includes/admin/class-gen-history-query.php — paginated run list + per-run I/O queries
- includes/admin/class-gen-history-page.php — hidden admin page + asset enqueue
- assets/css/gen-history.css, assets/js/gen-history.js — table + drill-down UI
- templates/admin/gen-history-page.php — HTML template
### Per-step input + output drill-down (Stage I/O)
'Stage I/O' toggle opens an AJAX-loaded inline panel. For each generation_log stage:
- input_system: system-role message content from generation_log.request_json
- input_user: last user-role message from request_json (rendered instruction)
- output: model response from run_stages.meta_json.output
- output_pruned: true when meta_json present but output key absent (retention prune)
New AJAX endpoint: prautoblogger_gen_run_io (manage_options + nonce)
New file: includes/ajax/class-gen-run-io-handler.php
### gen-log INPUT/OUTPUT coverage verified
- INPUT: generation_log.request_json stores the chat body WITHOUT the Authorization
header (Request_Recorder captures body after build_body(), before headers are added).
System + user message content extracted and surfaced.
- OUTPUT: run_stages.meta_json.output is the LLM response text (Run_Stage_State::done()).
Image/log-only stages (image_a, image_b, llm_research, image_prompt_rewrite) have no
run_stages row; output is null, NOT pruned. Pruned outputs are flagged honestly.
### Dossier as entry point
Runs with a linked post show a 'Dossier' button deep-linking into the existing dossier
(which renders full per-stage I/O with editorial context). Stage I/O is complementary —
works for failed/orphan runs and provides the same data in a lighter inline UI.
### Stage-list-driven
Stage labels via Stage_Display_Map::label() — Phase 2b curate/seo appear automatically.
### Dead prautoblogger_pipeline_history endpoint
Not dead — it IS used by pipeline-settings.js for prompt version history (M3 feature).
Left in place. No dangling endpoint.
## M3 P2 sweep
- Removed dead data-preview-nonce and data-diff-nonce HTML attrs from
pipeline-settings-prompt-panel.php (JS uses prabPipeline.*Nonce from
wp_localize_script, not data-* attrs — confirmed by grep).
- Removed 3 redundant wp_create_nonce() calls from class-pipeline-settings-renderer.php
(preview_nonce, history_nonce, diff_nonce keys that fed the now-removed dead attrs).
- Updated CONTEXT.md: added M3 + M4 terminology tables.
- Updated ARCHITECTURE.md: added ajax/ section to file tree; M3/M4 design notes.
- Updated INFORMATION-ARCHITECTURE.md: prautoblogger-gen-history entry.
## Security
- manage_options cap + prautoblogger_gen_run_io nonce on the AJAX endpoint.
- All output esc_html()/esc_url() server-side before JSON-encoding.
- JS renders prompt/response text via textContent — never innerHTML.
- No writes from history UI; request_json never contains auth headers.
## Version: v0.25.0 → v0.26.0
Agent-Session: pipeline-settings-m4
Relocates Content/AI Models/Sources fields into the per-step Pipeline panels; adds a Global Content Context block; retires the 3 duplicate Settings tabs. Option keys unchanged (no migration). QA APPROVE for 80d8e5f; CI green.
Agent-Session: pipeline-settings-m2-merge
Pipeline v2 Phase 1, commit 3/4. Cost_Governor reserves each call's
worst-case estimate (prompt chars/4 + max_tokens via the #18 pricing
chain) against the run ledger row with one atomic conditional UPDATE
(the #10 lock discipline) BEFORE dispatch, then settles to actuals after
each response; failed calls release their hold. Integrated inside
OpenRouter_Provider::send_chat_completion so EVERY text-LLM call path is
guarded with zero call-site changes; the image curl_multi batch reserves
its summed estimate in Image_Pipeline before generate_image_batch.
Breach: run halted (sticky), overage recorded, Cost_Ceiling_Exception
aborts the un-dispatched call, process_next_queued_article aborts the
remaining queued articles of halted/failed runs, and the Review Queue
shows a 'Halted runs' notice (runs halted pre-post have no draft to
list). Ceiling = prautoblogger_per_run_cost_ceiling_usd setting (default
constant $0.50, snapshotted at run start, 0 = disabled). Standalone
no-run calls, the monthly cap, and the AI Gateway path are unchanged.
Under-300: provider API-key fetch+validation moved verbatim to
OpenRouter_Request_Builder::resolve_api_key().
Agent-Session: pipeline-v2-phase1-engineer-20260612
Each idea row now has a Generate button that triggers single-article
generation via a background WP-Cron job. Skips the pipeline's collect/
analyze/score steps and goes straight to Article_Worker with the
selected idea. Button shows real-time stage updates via per-idea status
transients, then swaps to Edit/View links with cost on completion.
Respects the generation lock to prevent concurrent runs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New submenu page showing all article ideas from the content analyzer.
Displays title, type, relevance score, frequency, key points, and
keywords with type filtering and pagination. Gives live visibility
into the idea pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract read-only reporting methods from PRAutoBlogger_Cost_Tracker into a new
PRAutoBlogger_Cost_Reporter class. This reduces Cost_Tracker from 313 to 206
lines and Reporter to 141 lines, both under the 300-line cap.
Changed files:
- Create includes/core/class-cost-reporter.php with reporting methods:
* get_monthly_spend(): float
* get_daily_spend(int $days = 30): array
* get_spend_by_stage(string $start_date, string $end_date): array
* get_budget_utilization(): float
- Update includes/core/class-cost-tracker.php:
* Remove reporting methods
* Update is_budget_exceeded() and would_exceed_budget() to use Cost_Reporter
* Keep get_current_run_cost() (tracks current pipeline run only)
* Add @see cross-reference to Cost_Reporter
- Update templates and admin classes to instantiate Cost_Reporter:
* templates/admin/metrics-page.php
* templates/admin/settings-page.php
* includes/admin/class-admin-notices.php
* includes/admin/class-dashboard-widget.php
- Split tests/unit/Core/CostTrackerTest.php:
* Create tests/unit/Core/CostReporterTest.php for reporter methods
* Keep run ID and budget enforcement tests in CostTrackerTest
Autoloader automatically discovers PRAutoBlogger_Cost_Reporter class.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Save Settings shows "Saving…" on button + green success banner after
redirect (auto-dismisses after 6s)
- Test Connections shows spinning icon + progress banner while in-flight
- Generate Now shows spinning icon + timed pipeline stage messages so
the user knows the pipeline is working, not hung
- Shared ab-btn-loading CSS class with smooth dashicon spin animation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WordPress plugin that auto-researches topics from Reddit, generates
SEO-optimized articles via OpenRouter LLMs, runs them through a chief
editor agent, and publishes on a daily schedule.
Includes:
- Full generation pipeline (collect → analyze → score → generate → review → publish)
- Cost tracking and budget enforcement
- Admin UI: settings, review queue, metrics, activity log
- CI/CD pipeline (GitHub Actions → WordPress REST deploy)
- Unit test suite (PHPUnit + Brain Monkey)
- Structured logging with configurable verbosity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>