212 PHPCS formatting errors auto-fixed by phpcbf across 18 files.
All remaining issues are warnings (exit 2), not errors — CI treats
exit 2 as success via the same pattern as peptide-news-plugin.
Also adds class-psa-ai-content.php and class-psa-directory-api.php to
tests/bootstrap.php require list — both files exist in includes/ but
were missing from bootstrap, causing 5 PHPUnit "Class not found"
failures in AIGeneratorTest and DirectoryTest.
Rendering methods (settings page, field renderers, usage summary,
batch script enqueue) moved to PSA_Admin_Page. PSA_Admin retains
registration, sanitization, migration, re-enrichment, and get_setting
(now public). Backward-compatible proxy methods on PSA_Admin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DeepSeek models on OpenRouter omit the usage object, causing all token
counts and costs to log as zero. Added character-based estimation
fallback (~3.75 chars/token), token_source column to distinguish
measured vs estimated data, and admin UI indicators (amber warnings,
~ prefix on estimates). Updated DeepSeek/Qwen pricing table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
is_budget_exceeded() used `0 === $budget` but floatval() returns float,
so 0 (int) === 0.0 (float) is always false. This caused unlimited
budget ($0) to be treated as exceeded, blocking all AI generation.
Same bug existed in the admin display (showed "$0.00" instead of
"Unlimited" for budget remaining).
This was a P1 bug that prevented ALL AI content generation since v4.3.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the WP-Cron-based re-enrichment with an AJAX polling approach
that processes one peptide per request. This bypasses WP-Cron entirely,
which is unreliable on Hostinger shared hosting with LiteSpeed + Cloudflare.
New files:
- includes/class-psa-batch-enrichment.php — AJAX endpoints for status + process
- assets/js/psa-batch-enrich.js — Admin progress bar UI with polling loop
The admin settings page now shows a "Re-enrich Peptides" button with live
progress bar, per-item log, and automatic retry on failure. Respects the
daily generation cap and monthly budget.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix multi-item associative array on single line in class-psa-admin.php
(line 273) and remove blank line before closing class brace in
class-psa-ai-generator.php (line 540).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move declare(strict_types=1) after file-level docblock in all 12 class
files to satisfy WordPress Coding Standards docblock placement rule.
Fix multi-line function call formatting in class-psa-admin.php. Align
array double arrows and equals signs. Fix escaped backticks in
AIGeneratorTest heredoc that broke markdown fence stripping tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds [peptide_directory] shortcode with card grid, category taxonomy (8 terms),
search, pagination, detail modal, and /v1/compounds REST endpoint. Includes 9
new meta fields (half-life, stability, solubility, etc.), admin data tools for
migration/re-enrichment, integration hooks for cross-plugin extensibility,
dark mode support, and full uninstall cleanup. Refactors AI generator into
PSA_OpenRouter + PSA_KB_Builder classes. Adds PHPUnit tests for PostType and
Directory classes. Fixes QA findings: dead JS selector, renderError loading
state, numeric meta validation, prepared SQL in uninstall.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create PSA_Cost_Tracker class with database table for API call logging
- Log prompt/completion tokens and estimated cost for every OpenRouter API call
- Implement monthly budget system with hard stop (budget of 0 = unlimited)
- Add budget checks in validate_peptide_name() and generate_peptide_content()
- Add dry_run() method to estimate costs before making API calls
- Display monthly usage summary and recent API logs in admin settings
- Add monthly_budget setting to admin interface with number input field
- Update uninstall.php to drop psa_api_logs table on plugin removal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create PSA_Encryption class with AES-256-CBC encrypt/decrypt methods
- Derive encryption key from wp_salt('auth') using SHA-256 hash
- Prepend IV to ciphertext and base64-encode for safe storage
- Update PSA_Admin to encrypt API keys on save, mask on display
- Preserve existing key when empty/mask value submitted (no overwrite)
- Update PSA_AI_Generator to decrypt API key on read from database
- Maintain backward compatibility with plaintext keys (auto-encrypt on next save)
- Prefer wp-config constant over database value for maximum security
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>