# 🔧 Installation Guide — All Cron Fixes

## Files Changed (4 patched + 1 new)

| File | Location | Change |
|------|----------|--------|
| `class-scheduler.php` | `includes/` | **PATCHED** — batch lock + time limit 600→1800 + try/finally |
| `class-newsbot-scheduler.php` | `newsbot/` | **NEW FILE** — PE cron hook, batch lock, publish datetime, day gating |
| `class-kaizen.php` | `includes/` | **PATCHED** — engine filtering (PE only learns from PE corrections) |
| `class-newsbot-generator.php` | `newsbot/` | **PATCHED** — batch lock in AJAX, time limit 900→1800, Kaizen `'pe'` filter, 429 Retry-After |

---

## Step 1: Upload Files

1. **Replace** `includes/class-scheduler.php` with the patched version
2. **Upload** `newsbot/class-newsbot-scheduler.php` (new file — goes in the `newsbot/` folder)
3. **Replace** `includes/class-kaizen.php` with the patched version  
4. **Replace** `newsbot/class-newsbot-generator.php` with the patched version

## Step 2: Wire the PE Scheduler into Your Main Plugin File

In your main plugin entry file (likely `damalion-blog-ai.php` or `index.php`), you need to:

### A) Require the new file

Find where other newsbot files are required and add:

```php
require_once __DIR__ . '/newsbot/class-newsbot-scheduler.php';
```

### B) Initialize on plugin load

Find where other singletons are initialized and add:

```php
DBA_NewsBot_Scheduler::get_instance();
```

### C) Wire activation/deactivation hooks

Find your `register_activation_hook` / `register_deactivation_hook` section and add:

```php
// In activation:
DBA_NewsBot_Scheduler::activate();

// In deactivation:
DBA_NewsBot_Scheduler::deactivate();
```

### D) Re-schedule on settings save

In `class-newsbot-admin.php`, after `register_setting()` processes a save, add:

```php
// After sanitize() runs and settings are saved:
DBA_NewsBot_Scheduler::maybe_reschedule();
```

Or add it as a hook:
```php
add_action('update_option_dba_pe_settings', array('DBA_NewsBot_Scheduler', 'maybe_reschedule'));
```

## Step 3: Set Up Real Server Cron

**WordPress cron only fires when someone visits the site.** For reliable 10:00 AM NY publishing, you MUST set up a real server cron.

### Option A: cPanel Cron Job

In cPanel → Cron Jobs, add:

```
*/5 * * * * wget -q -O /dev/null "https://YOUR-DOMAIN.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1
```

This hits WP-Cron every 5 minutes, ensuring both core (06:00 UTC) and PE (15:00 UTC) hooks fire on time.

### Option B: Direct WP-CLI (if available)

```
*/5 * * * * cd /path/to/wordpress && wp cron event run --due-now >/dev/null 2>&1
```

### Option C: Disable WP Internal Cron (recommended with server cron)

Add to `wp-config.php`:

```php
define('DISABLE_WP_CRON', true);
```

This prevents WP from firing cron on every page load (saves performance) since the server cron now handles it.

## Step 4: Verify

After uploading and activating:

1. Go to **PE News Bot** admin page → check "Next run" shows correct time
2. Click **"Run PE Batch Now"** → verify batch starts and completes
3. Check error log for: `DBA PE Scheduler: Cron scheduled — next run: ...`
4. Try clicking "Run PE Batch Now" twice quickly → second click should show "batch already running" error

### Verify cron is registered:

Install "WP Crontrol" plugin and check for these two hooks:

| Hook | Schedule | Next Run |
|------|----------|----------|
| `dba_daily_generation` | daily | ~06:00 UTC |
| `dba_pe_daily_generation` | daily | ~15:00 UTC |

---

## What Each Fix Does

### 1. Batch Lock (class-scheduler.php + class-newsbot-scheduler.php + class-newsbot-generator.php)

**Before:** If someone clicks "Run PE Batch Now" during a scheduled cron run, both execute simultaneously → race condition on dedupe, double posts, corrupted progress transient.

**After:** A transient lock (`dba_pe_batch_lock`) is acquired before any batch starts. If a second batch attempts to start, it gets rejected. The lock auto-expires after 30 minutes as a safety net. Both the AJAX manual trigger and the cron callback check the same lock.

### 2. Time Limit 900→1800 (class-newsbot-generator.php + class-scheduler.php)

**Before:** `set_time_limit(900)` = 15 minutes. With 7 geos × ~2 min per post + retries + roundup = easily 16+ minutes → timeout.

**After:** `set_time_limit(1800)` = 30 minutes. Comfortable margin even with retries and slow API responses.

### 3. Kaizen Engine Filtering (class-kaizen.php + class-newsbot-generator.php)

**Before:** PE system prompt receives editor corrections from Luxembourg articles. EU prompts receive corrections from US PE articles. Cross-contamination wastes retries when EU link patterns appear in US validator checks.

**After:** `DBA_Kaizen::get_learning_context('pe')` queries ONLY posts tagged with `_dba_pe_auto_created`. PE learns from PE editor corrections only. Each engine has its own cache key (`dba_kaizen_context_pe`).

### 4. 429 Rate-Limit Detection (class-newsbot-generator.php)

**Before:** On 429, the generator retries with fixed 5s/15s backoff. If OpenAI says "wait 60 seconds", the retry comes too soon → another 429 → burns all retries.

**After:** On 429, the generator reads the `Retry-After` header from OpenAI's response and waits that exact duration (capped at 120s, default 30s). Applied to both research and article generation API calls.
