Files
prautoblogger/CHANGELOG.md
peptiderepo 86ceea0da1 Fix enabled-sources checkbox not persisting when all unchecked
Browser omits checkbox fields from POST when none are checked, so the
prautoblogger_enabled_sources option retained its previous value (with
Reddit). Added a hidden input to the checkboxes renderer so an empty
value is always submitted. Also replaced the hardcoded "Collecting
sources from Reddit…" pipeline status with a dynamic message reflecting
actually-enabled sources.

Bump 0.6.2 → 0.6.3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 12:18:59 +07:00

409 lines
21 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to PRAutoBlogger will be documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project uses [Semantic Versioning](https://semver.org/).
## [0.6.3] — 2026-04-20
### Fixed
- **Unchecking all sources still used Reddit.** Browser omits checkbox fields
from POST when none are checked, so the `prautoblogger_enabled_sources`
option never updated. Added a hidden input so an empty value is always
submitted — unchecking all sources now correctly saves `[]`.
- **Pipeline status hardcoded "Reddit".** The "Collecting sources…" stage
message now reflects the actually-enabled sources instead of always
saying "Reddit".
## [0.6.2] — 2026-04-20
### Added
- **Deterministic peptide auto-linker.** New `Peptide_Linker` class scans
generated HTML and wraps every mention of a known peptide in a hyperlink
to its `/peptides/{slug}/` database page. Runs as a post-processing step
in Publisher before `wp_insert_post` — no LLM involvement, 100% reliable.
Handles case-insensitive matching, hyphen/space variants (e.g., "BPC-157"
and "BPC 157"), and skips text already inside `<a>` tags. Gracefully
no-ops if PR Core is not active.
### Changed
- **Removed prompt-based peptide linking.** The system prompt no longer asks
the LLM to link peptides (unreliable). Peptide links are now injected
deterministically by `Peptide_Linker` after content generation. The prompt
still provides article links and the "never fabricate URLs" rule.
## [0.6.1] — 2026-04-20
### Added
- **Peptide auto-linking.** When any peptide name from the PR Core database is
mentioned in a generated article, the first mention is automatically linked to
the corresponding `/peptides/{slug}/` page. The system prompt now includes a
complete peptide database reference list alongside the existing article links.
Gracefully degrades if PR Core is not active (`post_type_exists()` guard).
### Changed
- **Extracted prompt builders into Content_Prompts class.** Moved all prompt
construction (system prompt, stage prompts, linking rules) from
`Content_Generator` into a new `Content_Prompts` static helper. Generator
dropped from 324 → 162 lines; prompts class is 252 lines. Both under 300.
- **Internal link rules strengthened.** Linking rules section now explicitly
instructs the model to link every peptide's first mention and never fabricate
URLs, with real published article and peptide page URLs provided.
## [0.6.0] — 2026-04-20
### Added
- **Generate article from idea.** New "Generate" column in the Ideas browser.
Click any idea's Generate button to produce an article directly, bypassing
the scheduled pipeline's collect → analyze → score steps. The button shows
real-time stage updates (drafting, editing, publishing) via per-idea status
polling, then swaps to Edit/View links with cost on completion.
- `Ideas_Browser::on_ajax_generate_from_idea()` — AJAX trigger that stores
the idea and schedules a one-shot WP-Cron event.
- `Ideas_Browser::on_cron_generate_from_idea()` — Background handler that
runs Article_Worker for the selected idea.
- `Ideas_Browser::on_ajax_idea_gen_status()` — Per-idea status polling.
- `assets/js/ideas-browser.js` — Button click, AJAX, polling, and UI state.
- Per-idea transients (`prab_idea_gen_{id}`) for independent status tracking.
- Page-load polling resume for ideas that were mid-generation.
- Generation lock respected — only one generation at a time.
## [0.5.3] — 2026-04-20
### Fixed
- **Writing instructions not obeyed by LLM.** The user-configured writing
instructions (bullet points, hyperlinks, style rules) were appended to the
system prompt as a weak "Additional instructions:" afterthought. Models
prioritized competing format directives in the user prompts and ignored
the style guide.
- System prompt now frames writing instructions as a "MANDATORY STYLE GUIDE"
with explicit override language.
- Every stage's user prompt (outline, draft, single-pass) now includes a
reinforcement line: "Follow EVERY requirement from your system prompt
style guide."
- Polish stage explicitly preserves bullet points, numbered lists, and
hyperlinks instead of flattening them.
- Chief editor's revision rules now preserve structural formatting (lists,
links) from the original draft.
## [0.5.2] — 2026-04-20
### Added
- **LLM research cost amortization.** The research LLM call (which runs once
per pipeline execution) is now divided evenly across all articles produced
in that run. Each article's cost breakdown popover shows its amortized share
of the research overhead. Previously, research costs were orphaned with no
post attribution.
- `Post_Assembler::amortize_research_costs()` — Queries the unlinked
research log entry, divides cost and tokens by article count, inserts one
row per article, and removes the original.
- `link_generation_logs()` now excludes `llm_research` stage from per-article
linking so the cost isn't grabbed wholesale by the first article.
- `Source_Collector::set_cost_tracker()` — Accepts the pipeline's cost
tracker so research costs are tagged with the pipeline's `run_id`.
- `LLM_Research_Provider` now uses the pipeline's cost tracker when available,
ensuring research log entries share the same `run_id` as article entries.
- Amortization runs after all articles complete (both single-article and
chained-job paths in `Pipeline_Runner`).
## [0.5.1] — 2026-04-20
### Added
- **Ideas browser admin page.** New "Ideas" submenu under PRAutoBlogger showing
all article ideas collected by the content analyzer. Displays suggested title,
topic, type (question/complaint/comparison/news/guide), relevance score bar,
frequency, key points, and target keywords. Filterable by type with count
badges. Paginated at 30 per page, newest first.
## [0.5.0] — 2026-04-20
### Added
- **LLM Deep Research source provider.** New data source that sends a configurable
research prompt to a reasoning-capable model (e.g. Grok 4.1 Fast, DeepSeek-R1)
to identify trending topics, emerging questions, misconceptions, and content gaps.
Findings feed into the content analyzer alongside Reddit data, giving the pipeline
both real-time community signals AND deep knowledge-base research.
- `includes/providers/class-llm-research-provider.php` — Source provider implementation.
- New admin settings in Sources tab:
- "LLM Deep Research" checkbox in Enabled Sources.
- "Research Model" model picker — pick a reasoning-capable model.
- "Research Prompt" textarea — customizable research brief with `{niche}` placeholder.
- Each research run produces 8-12 findings stored as `llm_research` source data.
- Deduplication via date-based source IDs (one fresh run per day, refreshes on re-run).
- Cost tracked as `llm_research` stage in generation logs.
- Content analyzer now formats LLM research findings with a cleaner label
(`[LLM Research] Relevance: N`) instead of the Reddit-specific `r/subreddit` format.
## [0.4.9] — 2026-04-19
### Added
- **OpenRouter reasoning mode support.** New "Enable Reasoning" toggle and
"Reasoning Effort" selector in AI Models settings. When enabled, sends
`reasoning: {enabled: true, effort: "<level>"}` to models that support it
(Grok 4.1 Fast, DeepSeek-R1, etc.). Effort levels: Extra High, High,
Medium, Low, Minimal. Reasoning tokens are billed as output tokens — cost
is automatically tracked. Models that don't support reasoning ignore the
parameter. Per-call override available via the `reasoning` key in the
provider options array.
- Response parser now captures `reasoning_tokens` and `reasoning_content`
from the OpenRouter response for downstream logging and debugging.
## [0.4.8] — 2026-04-19
### Fixed
- **GLM 4.7 Flash timeout during analysis.** The model was timing out at 120s on
large analysis prompts (especially with the new custom instructions). Bumped
API timeout from 120s to 180s.
- **Empty raw response in error logs.** Parse failure errors now include the raw
LLM response inline (first 500 chars) at error level, not debug level, so
the actual failure reason is always visible in the Activity Log.
## [0.4.7] — 2026-04-19
### Added
- **Cost breakdown popover.** The Cost column in the post list is now clickable.
Hovering or clicking shows a per-step breakdown: stage name, model used, token
count, and cost for every LLM call that produced the article.
## [0.4.6] — 2026-04-19
### Added
- **Image B toggle.** New "Generate Second Image (B)" toggle in Images settings.
Disabling saves one image generation call + one LLM prompt rewrite per article.
Defaults to enabled for backwards compatibility.
## [0.4.5] — 2026-04-19
### Added
- **Analysis Instructions setting.** Custom instructions appended to the analysis
LLM's system prompt. Steers how source data is evaluated and which topic ideas
get surfaced.
- **Editor Instructions setting.** Custom instructions appended to the chief
editor's system prompt. Controls what the editorial review looks for and how
it decides to approve, revise, or reject articles.
## [0.4.4] — 2026-04-19
### Added
- **Writing Instructions setting.** New textarea in Content settings lets you
provide custom instructions appended to the LLM's system prompt when writing
articles. Use it to steer style, structure, voice, and formatting without
editing code.
## [0.4.3] — 2026-04-19
### Fixed
- **"Analysis response was not valid JSON" with some models.** Models like
GPT 5.1 Nano ignore `response_format: json_object` and wrap JSON in markdown
fences or preamble text. New `PRAutoBlogger_Json_Extractor` utility strips
fences and extracts the outermost JSON object. Applied to both the content
analyzer and chief editor parsers. Raw response now logged on failure for
debugging.
## [0.4.2] — 2026-04-19
### Fixed
- **Posts list Title column unreadable.** Title was compressed to ~30px by the
three custom columns. Title now gets 35% minimum width with word-wrap enabled.
Model columns trimmed to 100px with text-overflow ellipsis for long model names.
## [0.4.1] — 2026-04-19
### Added
- **Posts list columns: Writing Model, Image Model, Cost.** Three new columns
on the WordPress Posts admin page. Writing Model shows the LLM that wrote the
article, Image Model shows the model that generated the featured image, and
Cost shows the total API spend for generating that article (summed from the
generation log). All columns show "—" for non-generated posts. Writing Model
is sortable. Model names are shortened (provider prefix stripped) with full
ID in a tooltip.
## [0.4.0] — 2026-04-19
### Added
- **Semantic dedup via embedding cosine similarity.** Replaces keyword-overlap
dedup (60% word match) with MiniLM-L12-v2 embeddings via OpenRouter. Catches
rephrasings like "BPC-157 dosing" vs "how much BPC-157 to take" that keyword
matching misses. Automatic fallback to keywords if embedding API unavailable.
Cost: ~$0.00001 per generation run.
- `includes/core/class-semantic-dedup.php` — Dedup engine with embedding +
keyword fallback.
- `includes/providers/class-open-router-embedding-provider.php` — OpenRouter
`/embeddings` client with batch support and cosine similarity helper.
- **LLM-aware topic avoidance.** Analysis prompt now includes the last 30 days
of published article titles with explicit "do not suggest similar topics"
instruction. Zero additional cost — appended to the existing analysis call.
### Changed
- Dedup window widened from 7 days to 30 days (semantic similarity is precise
enough to avoid over-blocking).
- `class-idea-scorer.php` refactored to delegate dedup to `Semantic_Dedup`.
### Fixed
- Featured images displayed at 300×300 (WordPress "medium" size) instead of full
width. Changed theme to use `the_post_thumbnail('full')` with responsive CSS.
- Race condition causing duplicate article generation when the AJAX status poller
re-scheduled a cron event for an article already being generated.
- False stall detection on multi-article runs (measured elapsed time from start
instead of idle time since last progress update).
## [Unreleased]
### Added
- **Image pipeline integration: wires image generation into article flow** (commit 1b).
Completes the image + Instagram A/B experiment workstream by adding three
orchestration classes that generate and sideload two images per published post.
- `includes/core/class-image-pipeline.php` — Orchestrates A/B image generation:
generates Image A (article-driven prompt) as featured image and Image B
(source-driven prompt) stored in post meta `_prautoblogger_image_b_id`. Each
image is independently fallible; article publishes even if both fail.
- `includes/core/class-image-prompt-builder.php` — Synthesizes visual prompts
from article title + first paragraph (for Image A) and Reddit thread title +
top comment (for Image B). Both prompts append the CEO-locked 90s infomercial
style suffix from settings. Prompts kept concise (under 200 words) for FLUX.1
generation quality.
- `includes/core/class-image-media-sideloader.php` — Downloads generated image
bytes, creates temporary file, imports via `media_handle_sideload()`, sets alt
text and generation metadata (`_prautoblogger_image_*`), cleans up temp files.
- Settings: `prautoblogger_image_enabled` (toggle, default off) — allows users to
enable/disable image generation after providing Cloudflare credentials.
- `PRAutoBlogger_Publisher::attach_generated_images()` — New private method that
runs the image pipeline post-creation and sets `_thumbnail_id` (Image A) and
`_prautoblogger_image_b_id` (Image B) post meta. Errors are logged but do not
block post publication.
- Tests: `tests/unit/Core/ImagePipelineTest.php` (orchestration, cost tracking,
graceful failure modes), `tests/unit/Core/ImagePromptBuilderTest.php` (prompt
generation from various article/source shapes, length limits).
- ARCHITECTURE.md: added new image pipeline step (6b) to data flow, three new files
to core/ section of file tree.
- **OpenRouter model registry (model-picker commit 1).** Foundation for the
smart model picker: fetches, normalizes, and caches the OpenRouter model
list (`/api/v1/models` — free, unauthenticated). Daily refresh via the
existing `prautoblogger_daily_generation` cron hook with 12h idempotency.
- `includes/services/interface-model-registry.php` — Phase 3-aware contract.
- `includes/services/class-open-router-model-registry.php` — fetch + cache + query.
- `includes/services/class-open-router-model-normalizer.php` — raw → standardized shape.
- Capability vocabulary: `text→text`, `text+image→text`, `text→embedding`, etc.
- Zero-coupling: no PRAUTOBLOGGER_* constants inside the class — Phase 2 lift
into a shared Composer package requires only a namespace rename.
- Cost impact: $0/month (free endpoint, no LLM tokens).
- **Image provider: Cloudflare Workers AI (FLUX.1 family).** Ships the provider, its
pricing + validator helpers, four new settings fields in a new "Images"
admin section, and unit tests with mocked HTTP.
- `includes/providers/interface-image-provider.php` — contract for any image provider.
- `includes/providers/class-cloudflare-image-provider.php` — FLUX on Workers
AI, direct call to `/accounts/{id}/ai/run/...` (bypassing AI Gateway per
decision D-001); exponential-backoff retries on 429 / 5xx / network,
loud fail on 4xx; handles both raw-bytes and JSON-envelope response shapes.
- `includes/providers/class-cloudflare-image-pricing.php` — model alias →
full Workers AI id resolution + per-megapixel cost estimation.
- `includes/providers/class-cloudflare-image-validator.php` — non-destructive
"Test Connection" credential check that never generates a real image.
- Settings: `prautoblogger_cloudflare_ai_token` (encrypted),
`prautoblogger_cloudflare_account_id`, `prautoblogger_image_model`
(schnell / dev), `prautoblogger_image_style_suffix` (default = CEO-locked
90s infomercial prompt).
- Constants: `PRAUTOBLOGGER_DEFAULT_IMAGE_MODEL`,
`PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX`.
- Tests: `tests/unit/Providers/CloudflareImageProviderTest.php`.
- ARCHITECTURE.md: new key decision #16 (image pipeline: Cloudflare Workers
AI), new external API integration row, new options rows, file tree updates.
- CONVENTIONS.md: new "How To: Add a New Image Provider" section.
## [0.2.2] — 2026-04-12
### Changed
- **CI/CD pipeline now runs PHPCS and PHPUnit** before deploying.
Added `shivammathur/setup-php@v2` for PHP 8.1, Composer install,
WordPress Coding Standards check, and full PHPUnit test suite.
PHPCS runs in report-only mode (`|| true`) while codebase is
brought into full compliance; PHPUnit failures block deploy.
- Removed stub source providers (TikTok, Instagram, YouTube).
These were dead code — not registered in Source_Collector, threw
RuntimeException if enabled, and confused AI agents reading the
codebase. The `Source_Provider_Interface` remains the contract for
adding future platforms (see CONVENTIONS.md).
- Removed "coming soon" checkboxes from Enabled Sources setting.
- Updated ARCHITECTURE.md: removed stub file references, added
cross-system LLM budget coordination section documenting how
PRAutoBlogger and Peptide News share an OpenRouter account.
### Improved
- **PublisherTest rewritten with real behavioral assertions.**
Now tests: post_status ('publish' vs 'draft'), generation metadata
storage, title from idea, taxonomy assignment, tag assignment,
run_id-based log linking, RuntimeException on WP_Error, action/filter
hook firing. Replaces previous method-existence-only checks.
## [0.2.1] — 2026-04-12
### Changed
- **Reddit data source switched to RSS primary, .json fallback.**
PullPush.io was frequently stale/unavailable. Reddit RSS/Atom feeds work
reliably from datacenter IPs (Hostinger) where .json gets 403. Comments
are still fetched via .json (unavailable in RSS).
- Updated "Enabled Sources" checkbox label to "Reddit (RSS + .json)".
- Updated "Test Connections" to show RSS + .json status.
- Source status indicator now shows Reddit RSS (Primary) and .json (Fallback).
### Removed
- `class-pull-push-client.php` — replaced by RSS feeds in `class-reddit-json-client.php`.
- All PullPush.io references from admin UI, docs, and provider code.
### Fixed
- Generate Now button now always sends `force: '1'` to clear stale generation locks.
- Fixed encryption double-encryption bug with `enc:` prefix detection.
## [0.2.0] — 2026-04-12
### Changed
- **Reddit data source migrated from OAuth to RSS + .json (no auth required).**
Reddit rejected our API application; RSS feeds and .json endpoints are free
and require no authentication.
- Removed Reddit Client ID and Client Secret fields from admin API Keys tab.
- Updated "Test Connections" results to show Reddit source status.
### Added
- Source status indicator in API Keys tab showing RSS and .json
availability with live status dots and last-collection timestamp.
- Configurable research cache TTL (1–72 hours) in Sources tab.
- Reddit time window selector (24h / week / month) in Sources tab.
- Posts-per-subreddit limit (5–100) in Sources tab.
- LiteSpeed cache purge step in CI/CD deploy pipeline.
### Removed
- `class-reddit-api-client.php` — replaced by `class-reddit-json-client.php`.
- Reddit OAuth credential fields and encrypted storage entry.
### Fixed
- Deploy pipeline now purges LiteSpeed/OPcache after file extraction,
preventing stale PHP bytecode from being served.
## [0.1.0] — 2026-04-10
### Added
- Initial plugin scaffold with WordPress-native architecture.
- OpenRouter LLM provider with configurable model selection and retry logic.
- Reddit API source provider (OAuth2 script app) for collecting posts and comments.
- Content analysis engine: detects recurring questions, complaints, and comparisons.
- Idea scorer with deduplication against existing posts.
- Configurable writer agent pipeline (single-pass or multi-step: outline → draft → polish).
- Chief editor agent for LLM-powered editorial review before publishing.
- Publisher with full generation metadata stored as post_meta.
- Cost tracker with per-call logging, monthly budget enforcement, and hard-stop.
- Metrics collector: WordPress native + GA4 integration + composite content scoring.
- Admin settings page with sections for API keys, models, content, sources, schedule, and analytics.
- Metrics dashboard showing monthly spend, budget utilization, cost by stage, and daily spend.
- Post metabox showing generation metadata on PRAutoBlogger-generated posts.
- Admin notices for missing API keys, budget warnings, and configuration hints.
- Stub providers for TikTok, Instagram, and YouTube (future implementation).
- Encrypted storage for API keys using AES-256-CBC with wp_salt().
- AJAX-powered "Generate Now" and "Test Connections" buttons.
- Custom database tables for source data, analysis results, generation logs, and content scores.
- Clean uninstall handler removing all plugin data.
- ARCHITECTURE.md and CONVENTIONS.md for AI-readable codebase documentation.