1bc01c08bc
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 8s
Universal: PR Check / Wiki Update Reminder (pull_request) Successful in 6s
Universal: PR Check / Require Docs Update (pull_request) Failing after 8s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 13s
Universal: PR Check / Secret Scan (pull_request) Successful in 12s
Generic: Repo Health / Access control (pull_request) Successful in 4s
Universal: Build & Release / Promote to RC (pull_request) Failing after 22s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 17s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 16s
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m0s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Every non-interactive backup trigger used a slightly different path, so final status and notification gating behaved inconsistently depending on what started the backup (this is why pre-update backups did not gate notifications the way "Backup Now" does). Add src/Service/BackupRunner.php as the single synchronous entry point. It delegates to BackupEngine (which already sends notifications gated by the profile's notify_on_success/notify_on_failure and applies retention — so there is exactly ONE notification layer), and normalises the result, including the real complete/warning/fail status the engine records but did not return. BackupRunner self-loads BackupEngine so callers require one file. Routed through BackupRunner: - plg_system runPreActionBackup() (preaction) + web-cron handler (webcron) - plg_content triggerAutoBackup() (pre-install) - plg_task scheduled task run - plg_console RunCommand (cli) The dashboard AJAX/stepped "Backup Now" flow is unchanged (it is the interactive equivalent). The installer progress popup is deferred — the installer request cannot drive the AJAX modal (TODO(#196) noted in code). php -l clean on all changed files. Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i