# Changelog All notable changes to `laravel-workkit` are documented here. This project follows [Keep a Changelog](https://keepachangelog.com/) and keeps every release backward-compatible with the previous minor (see `PRINCIPLES/laravel-composer-packages.md` § Backward compatibility). ## Unreleased ### Added - **`workkit:stats`** (+ `StatsService`) — terminal "admin overview": per-Eloquent-model row counts (fast `information_schema` estimate on MySQL, `--exact` for `COUNT(*)`), largest tables by size, total database size, and a compact queue + backup summary. Models are auto-discovered from `app/Models` or `config('workkit.stats.models')`. `--json` for machine consumption; degrades gracefully on non-MySQL drivers. - **`workkit:queue:health`** (+ `QueueHealthService`) — per-queue depth / due / delayed / reserved counts and oldest-due-job age for the database queue driver, failed-jobs totals (+ last *N* hours), and STALLED-queue detection (due work past `max_age_minutes` with nothing reserved → worker likely down). Exits non-zero on any breach for cron/monitoring; thresholds under `config('workkit.queue.*')` with per-queue depth overrides. `QueueHealthService::evaluate()` is a pure, unit-testable function. - New config blocks `workkit.stats.*` and `workkit.queue.*` (read with explicit code-side defaults, so consumers who published an older config are unaffected). ### Added — backups - **`workkit:db:restore --fresh`** — drops every table + view in the target before importing, the only reliable fix for a restore failing with `errno 150` / error `3780` against a dirty or partially-migrated schema. The wipe is performed via the **same** `mysql` client and database the import uses (built from the connection's `$cfg`, scoped to `DATABASE()`), so a `url` DSN / `unix_socket` / read-write split can never make the wipe and the import target different databases. - **Pre-restore safety snapshot** — `--fresh` first takes *and verifies* a snapshot of the current database (named `*.pre-restore.sql.xz.enc`) and aborts before dropping anything if it can't be written; opt out with `--no-safety-backup`. On a post-wipe import failure the command prints the exact `workkit:db:restore --file=` recovery command. - **`workkit:db:verify`** (+ `workkit:db:backup --verify`) — proves a backup decrypts with this host's `APP_KEY` and is a complete, non-truncated xz stream, without touching the database; flags a missing mysqldump completion marker and compares the on-disk sha256 against the metadata sidecar. `--all` checks every backup. - **Metadata sidecar** — `workkit:db:backup` writes a best-effort `.meta.json` (size, sha256, created_at, cipher params) next to each backup. Never fails a good backup if it can't be written. - **`workkit:db:prune-backups --keep-min`** (config `backup.retention_min_keep`, default 5) — always keeps the N newest backups regardless of age, so an aggressive `--days` can never leave zero recovery points. Prune now also removes a backup's sidecar alongside it and sweeps orphaned sidecars. - **Restore safeguards** — the source backup is verified before any destructive step; a dirty (non-empty) target is reported with a `--fresh` hint; an import that incompatibly fails prints actionable guidance; a non-trivial backup that imports zero tables is flagged as a possible silent failure. The confirm line now shows the backup size. - New public `BackupService` helpers (all additive): `verify()`, `dropAllTables()`, `countTables()`, `estimateDatabaseBytes()`, `backupFiles()`, `restoreCandidates()`, `metaPathFor()`, `humanBytes()`, `runResult()`. ### Changed - **`BackupService::decryptDecompressImport()` now prepends `SET FOREIGN_KEY_CHECKS=0;`** to the import stream so a hand-rolled dump lacking mysqldump's own header still imports into an empty database regardless of table order. Harmless for mysqldump output (which sets/restores its own session vars), and the whole import is one short-lived session so the relaxed check never leaks. This does **not** rescue a restore onto a dirty schema with incompatible column types — use `--fresh` for that. - File selection in `restore`/`prune` now ignores `*.meta.json` sidecars while still finding custom `--out`-named backups (it denies the sidecar suffix rather than allow-listing one extension). The `restore` auto-pick also skips `*.pre-restore.*` snapshots so a recovery snapshot of a known-bad database is never restored by accident. ### Notes - Existing `BackupService` method signatures and all config keys are unchanged; new behavior is additive or confined to the new `--fresh` path. New nested config keys are read with explicit code-side defaults, so a consumer who published an older `config/workkit.php` is unaffected. - The README is now backup-documented and brought up to the standard Blax OSS package skeleton (badges, feature list, Star History).