# Version 1.1.1 Changelog

**Release theme:** Stability & Trust  
**Release date:** 2026-02-18

This release improves lifecycle hygiene, notification determinism, runtime safeguards, and internal consistency. It does not add new monitoring features or change core architecture.

---

## Phase 1 — Lifecycle Hygiene

### Activation

- Cron events now use consistent Unix timestamps (`time()`) when scheduled, for reliable behaviour across server configurations.
- Weekly database optimization is scheduled automatically on first activation (no settings save required).
- Retry and autoresolve crons are delayed by 5 minutes on activation to prevent immediate backlog processing and stale notification replay.
- Existing installs that used the legacy optimize cron are migrated automatically on first load after update.
- Plugin self-activation now logs and notifies correctly (event registry is bootstrapped during the activation hook).

### Deactivation

- **Complete cleanup:** All Sentinel cron jobs are removed when the plugin is deactivated, including legacy hooks.
- Known Sentinel transients (fallback logs, retry queue, memory alerts, performance samples, license cache, etc.) are cleared so no Sentinel scheduled or cached work remains on the site.
- No orphaned cron entries are left behind.

### Uninstall

- **Full removal:** Deleting the plugin (with or without deactivating first) now removes all Sentinel data:
  - The correct database tables are dropped (`sentinel_logs`, `sentinel_logs_archive`, `sentinel_incidents`).
  - All Sentinel options and transients are deleted (explicit + strict wildcard).
  - Sentinel entries are removed from the WordPress cron option so no scheduled work remains.
  - Digest deduplication options (`sentinel_daily_digest_last_sent`, `sentinel_weekly_digest_last_sent`) are cleaned up.
- Previous uninstall logic targeted wrong table names; this is corrected.

### Cron Normalization

- A single canonical list of Sentinel cron hooks (`sentinel_get_cron_hook_names()`) is used for activation, deactivation, and uninstall.
- The legacy `sentinel_optimize_tables` cron is retired; one `sentinel_optimize_database` cron is used instead.
- Internal rules ensure any new cron hook is added to the list, scheduled on activation if needed, cleared on deactivation, and removed on uninstall.

---

## Phase 2 — Runtime Determinism

### Notification System

- **Mutual exclusion:** Only one email pathway fires per event. If the Security Alert pathway (Pathway A) sends, the Granular Notification pathway (Pathway B) is skipped for the same event call. This eliminates the root cause of duplicate emails on activation (2 incidents × 2 pathways = 4 emails).
- **Digest deduplication:** Daily and weekly digest processing now records a "last sent" timestamp. If a digest was recently sent (within 20 hours for daily, 5 days for weekly), the next cron run is skipped. Prevents duplicate digest emails from cron race conditions.
- **Retry safety verified:** The `sentinel_event_logged` action has no listeners, confirming that the retry queue cannot replay notifications.
- Verbose debug `error_log` statements removed from license bypass and email send paths. Only failure logs retained.

### Category & Priority Normalization

- **Rate limiter fixed:** Priority multiplier map changed from `error/warning/info` (which never matched any event) to `high/medium/low` (canonical priorities). Tiered rate limiting now works as intended.
- **Dead email filter fixed:** The granular notification check for "Error Events" was comparing `$priority === 'error'` (a priority that doesn't exist). Changed to `$category === 'errors'` to match the actual errors category.
- All 78 registered events verified — all have canonical priorities and categories.

### Queue & Storage Safeguards

- **Performance samples capped:** The `sentinel_performance_samples` transient is now capped at 2,000 entries (was unbounded — could grow to tens of thousands over 8 days on busy sites).
- **Memory analysis sampling:** `store_memory_analysis()` now writes to the database on ~1 in 10 requests instead of every request. Reduces DB operations from 2/request to ~0.2/request for memory trending.
- **Retry queue overflow warning:** When the retry queue exceeds 50 entries, a self-warning is logged before truncation.
- Log queue hard cap at 1,000 entries was already in place (verified).

### Time Math

- All `current_time('timestamp')` calls in cron scheduling replaced with `time()` across:
  - `sentinel.php` (activation, batch scheduling)
  - `class-sentinel-cleanup.php` (cleanup and optimization scheduling)
  - `class-sentinel-file-monitor.php` (file check scheduling)
- Cleanup anonymizer simplified from `strtotime("-N days", strtotime(current_time('mysql')))` to `time() - (N * DAY_IN_SECONDS)` — pure timestamp arithmetic.
- Batch cron callback no longer clears and reschedules on every run — only reschedules if the hook is missing.
- Retry queue entries now store `time()` instead of `current_time('mysql')`.

---

## Files Touched

- `sentinel.php` — version bump, activation, deactivation, notification mutual exclusion, cron scheduling, email filters, performance caps, retry queue
- `includes/class-sentinel-logger.php` — rate limiter priority map
- `includes/class-sentinel-cleanup.php` — cron scheduling timestamps, anonymizer time math
- `includes/class-sentinel-memory-monitor.php` — memory analysis sampling
- `includes/class-sentinel-digest-reports.php` — digest deduplication guards
- `includes/class-sentinel-file-monitor.php` — cron scheduling timestamp
- `uninstall.php` — digest option cleanup
- `admin/class-sentinel-admin.php` — lifecycle test page registration removed
- `readme.txt` — version and changelog updated

---

## Invariants After v1.1.1

1. An event is notified at most once per configured pathway per `sentinel_log_event()` call.
2. Activation does not replay historical notifications or process stale queues.
3. Every event entering Sentinel is validated and normalized before storage or notification.
4. Sentinel cannot silently degrade over time due to unbounded transient growth.
5. All cron scheduling uses UTC timestamps (`time()`), not locale-dependent values.
6. Deactivation and uninstall deterministically remove all Sentinel artefacts.
