The previous deploy served stale admin.js from browser cache because
the version string (used as cache-buster query param) was unchanged.
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>
Root cause of the 401 was a corrupted API key: the WordPress auth
salt changed after the key was encrypted, so decryption produced
garbage instead of sk-or-... OpenRouter interprets the garbled
Bearer token as "Missing Authentication header."
Adds the same sk-or- prefix check to the credential validation
method so users see a clear "re-enter your key" message instead
of a cryptic 401 error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hostinger's hosting environment appears to strip the Authorization
header from outgoing wp_remote_post requests before they reach the
cURL transport. This caused OpenRouter to respond with HTTP 401
"Missing Authentication header" even though the header was set in
the wp_remote_post args array.
Fixes:
- Add http_api_curl filter (priority 99) that re-sets the
Authorization header directly on the cURL handle, bypassing any
intermediate header stripping. Scoped to openrouter.ai URLs only.
- Applied to all three OpenRouter call sites: send_chat_completion,
validate_credentials_detailed, and get_available_models.
- Filter is cleaned up in a finally block to avoid leaking.
Also:
- Add pre-flight key format validation (must start with "sk-or-")
to catch corrupted decryption early with a clear error message.
- Fix retry condition: was `>= 429` (incorrectly retried 430-499),
now `=== 429` (only retry rate-limit).
- Add detailed diagnostic logging on 401/403 auth failures.
- Add .env.credentials to .gitignore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address CTO audit findings for PRAutoBlogger:
- CI pipeline now runs PHPCS (WordPress standards) and PHPUnit before deploy
- Removed TikTok/Instagram/YouTube stub providers (dead code, not registered)
- Rewrote PublisherTest with real behavioral assertions (post_status, metadata,
taxonomy, log linking, hooks) instead of method-existence checks
- Added cross-system LLM budget coordination docs to ARCHITECTURE.md
- Bumped version to 0.2.2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LLMs sometimes ignore format instructions and wrap content in markdown
code fences or full HTML document structures. Added sanitize_llm_content()
to the publisher that strips these before wp_kses_post() runs, preventing
raw ```html blocks and <html><head><body> wrappers from reaching post_content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PullPush.io index was frequently stale/unavailable. Reddit RSS/Atom feeds
work reliably from datacenter IPs (Hostinger) where .json gets 403.
- RSS is now the primary source for post fetching
- .json endpoints kept as fallback for posts and sole source for comments
- Deleted class-pull-push-client.php
- Updated admin UI, test connection handler, docs, and ARCHITECTURE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite encryption with "enc:" prefix to permanently eliminate
the double-encryption bug on settings save
- Add one-time migration to clear legacy non-prefixed encrypted values
- Rewrite content analyzer prompts to request N distinct diverse topics
across 5 categories (question, complaint, comparison, news, guide)
- Pass target_count*2 from pipeline to analyzer for headroom
- Add skip_dedup flag so manual Generate Now always produces content
- Generate Now button now sends force=1 to clear stale locks
- Rename OpenRouter provider files to PSR-4 convention (hyphenated)
- Switch default models to google/gemini-2.5-flash-lite
- Clean up reddit_debug log channel to standard 'reddit'
- Bump version to 0.2.1
Cost: ~$0.003/article with Gemini 2.0 Flash, ~34s pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove Reddit OAuth fields (Client ID/Secret) from admin settings
- Add source status indicator showing PullPush.io + .json fallback health
- Add configurable cache TTL, time window, and posts-per-subreddit settings
- Update Test Connections to report which Reddit source is active
- Bump version 0.1.0 → 0.2.0 across plugin header, constant, and readme
- Switch deploy pipeline from zip/REST API to SSH rsync with --delete
- Add LiteSpeed cache purge step to prevent stale OPcache after deploy
- Update CHANGELOG.md and readme.txt for 0.2.0 release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- bootstrap: add PRAUTOBLOGGER_MAX_RETRIES, API_TIMEOUT, RETRY_BASE_DELAY constants
- BaseTestCase: add wp_json_encode + home_url stubs, reset Logger singleton in tearDown
- CostTrackerTest: use openai/gpt-4o (in MODEL_PRICING), fix log_api_call args,
allow multiple wpdb inserts (Logger may also insert event_log rows)
- OpenRouterPricingTest: add wpdb mock with prefix for Logger access on unknown models
- OpenRouterProviderTest: test send_chat_completion throws RuntimeException when
no API key is configured (matches actual guard clause behavior)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The get_transient, set_transient, and delete_transient functions are now
stubbed in BaseTestCase::setUp() so all tests have them available by default.
Individual tests can still override with Functions\when() in their own setUp.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ContentAnalyzerTest: pass Cost_Tracker as 2nd constructor arg
- IdeaScorerTest: stub get_option for topic_exclusions
- CostTrackerTest: stub get_option for monthly_budget_usd
- OpenRouterPricingTest: stub get_transient with cached model data
- OpenRouterProviderTest: stub get_option for API key + get_transient
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All 14 test files updated to use correct constructor patterns (array-based
value objects, dependency injection for ContentAnalyzer, etc.) and verified
method names against source.
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>