{
  "slug": "bw-dev",
  "name": "BW Dev",
  "version": "1.17.2",
  "download_url": "https://plugins.bowden.works/wp-content/uploads/plugin-updates/bw-dev-1.17.2.zip",
  "download_hash": "sha256:9858621e93dfb60c0560c8c849632d9eb62fb311b006b663218c11fbde78a6b9",
  "download_size": 518694,
  "requires": "6.0",
  "tested": "",
  "requires_php": "7.4",
  "last_updated": "2026-06-10",
  "homepage": "https://plugins.bowden.works/bw-dev/",
  "author": "Bowden Works",
  "description": "Bowden Works dev toolkit — admin columns, favicon, sticky elements, post-link blocks, YouTube embed, menu visibility, SVG uploads, admin notes, and Flywheel auto-update support in one actively-maintained plugin. Required for several site features. See Settings → BW Dev → About.",
  "changelog": "## [1.17.2] - 2026-06-10\n\n### Fixed\n- **Request Firewall: stale stub didn't self-heal after a file push (fleet-rollout BLOCKER).** The mu-plugin stub only regenerated on `admin_init`, gated behind a 1-hour transient. A manual file upload (SFTP/rsync/`wp plugin update`) doesn't fire `upgrader_process_complete`, so the gate never cleared and the stub kept running the OLD logic indefinitely until an admin happened to load wp-admin after the transient expired. On a fleet rollout a site could report \"updated\" while silently running the previous firewall. **Fix:** the deployed `FIREWALL_VERSION` is now recorded in an autoloaded option, and a cheap `init` hook (front-end + admin) compares it against the running code on every request — a single autoloaded-option compare in the common case. On a mismatch it regenerates immediately, outside admin and outside the 1-hour gate (throttled to once per 5 min so a non-writable mu-plugins dir can't cause a redeploy storm; fails closed to a retry, never blocks pages). Also: the stub now redeploys on plugin (re)activation and immediately when firewall settings are saved.\n- **Request Firewall: settings saved in admin baked the stale settings cache.** The settings singleton caches the option at boot; reacting to `update_option_bw_dev_settings` in the same request re-baked the stub from the pre-save copy (rule/threshold changes appeared to not take effect until a second admin load). Added `BW_Dev_Settings::flush()` and call it before the immediate redeploy so saved rules/thresholds bake right away.\n\n### Added\n- **Request Firewall: `wp bw-dev firewall deploy` / `wp bw-dev firewall status` (WP-CLI).** The clean headless rollout path: push plugin files to a site, then `wp bw-dev firewall deploy` regenerates + verifies the stub with no browser hit; `status` (table or `--format=json`) reports stub version, in-sync state, counter-store backend, allowlist size/age, load-shedding state, live pressure, and per-site rule count. `deploy` refuses (with a clear warning) when the module is disabled.\n- **Request Firewall: per-site redirect/deny rules in the admin UI.** A \"Per-site rules\" textarea on the Request Firewall tab — one rule per line: `/old-path => /new-path/` (301 redirect; relative resolves to the site, or a full `https://` URL), a bare `/path` (410 deny), `#` comments. These are baked into the stub and applied **always** (not only under pressure), so a dead-but-valuable URL like newcap's `/kitchen-equipped` becomes a permanent cheap 301 to the homepage — replacing a hand-rolled per-site emergency-shield mu-plugin. No SFTP or filter code needed; saved rules take effect immediately. (The programmatic `bw_firewall_rules` filter still works and layers on top.)"
}
