{
  "slug": "bw-lead-ai",
  "name": "BW Lead Attribution Intelligence",
  "version": "1.2.1",
  "download_url": "https://plugins.bowden.works/wp-content/uploads/plugin-updates/bw-lead-ai-1.2.1.zip",
  "download_hash": "sha256:c40c3b2a1fbcabe5541df5127b1db0c893b05274f8738fdc33cb6b76f0a086d6",
  "download_size": 243073,
  "requires": "6.0",
  "tested": "",
  "requires_php": "7.4",
  "last_updated": "2026-07-26",
  "homepage": "https://plugins.bowden.works/bw-lead-ai/",
  "author": "Bowden Works",
  "description": "Capture traffic source, attribute it to every lead, and understand where your leads are coming from.",
  "changelog": "## [1.2.1] - 2026-07-26\n\n### Fixed\n- **Organic search and social referrers were never classified correctly.** This is\n  the significant one. `hostMatches()` only tested whether a referring hostname\n  *ended* with `.<configured value>` — true for the nonsense host `mail.google`,\n  false for the real-world `www.google.com`. So no entry in Default Referrer\n  Classification ever matched, and **every** untagged search or social visit fell\n  through to the catch-all: source became the full hostname and medium became\n  `referral`.\n\n  In practice a Google organic visit was recorded as `www.google.com / referral`\n  instead of `google / organic`, and its channel resolved to the bare hostname\n  instead of \"Google Organic\" / \"Organic Search\". Facebook, Bing, LinkedIn and every\n  other configured host behaved the same way. Only tagged traffic (UTMs, click IDs)\n  and genuinely direct traffic were unaffected — which is why it went unnoticed:\n  acceptance criterion R3 had never been run, and the `{bw:source} : */referral`\n  channel rule was quietly absorbing all of it and looking plausible.\n\n  Matching now handles both config styles properly: a value with a dot matches the\n  host exactly or as a suffix (`openai.com` matches `chat.openai.com`), and a bare\n  value matches any whole dot-separated label (`google` matches `www.google.com` and\n  `google.co.uk`, but not `notgoogle.com`). As a side effect, Facebook's link shims\n  (`l.facebook.com`, `m.facebook.com`) now classify as social too. Covered by 15 unit\n  checks.\n\n  **What this changes for existing sites:** organic and social leads captured from\n  now on will show the correct source and medium. Leads already captured keep\n  whatever was stored in that visitor's browser at the time.\n\n### Added\n- **Simulated test links** — a new accordion in the Test tab with a ready-made set\n  of 20 incoming links covering every traffic type the plugin has to tell apart:\n  fully tagged campaigns, one row per configured click ID, minimal and untagged\n  URLs, referrer-driven arrivals, and edge cases (UTM + gclid conflict, this site's\n  own non-UTM aliases, custom dimensions).\n- Enter a landing path and every link regenerates live against the current domain.\n  Each row shows what it **resolves to** — channel, source / medium, campaign, click\n  ID, custom dimensions — computed against *this site's* actual settings, so the\n  panel doubles as a configuration check. Copy and Open buttons per row.\n- Scenarios are generated from live config, so a site with a customised click-ID\n  table, non-UTM aliases or custom dimensions gets links matching its own setup.\n- Referrer-driven scenarios are marked **preview only** and deliberately have no\n  Open button: a link cannot forge a referrer, so opening one would record a Direct\n  visit and quietly show the wrong result.\n- `{bw:channel}`, `{bw:first_channel}` and the interaction tags are now listed in\n  the Test tab's resolved-merge-tag table. `channel` had been missing since 0.8.x.\n\n### Changed\n- `BWLeadAI.simulateUrl( url, referrer )` takes an optional second argument to\n  simulate arriving from an external site. `resolveChannel` and `hostMatches` are\n  now exposed on the public API; `events.js` uses the shared `hostMatches` so social\n  matching and referrer classification can't drift apart."
}
