Skip to content

Release History ​

The changelog of record is GitHub Releases on racerfish/evolve-cms. This page is a condensed copy, updated as part of each release (see RELEASING.md in the package repo).

Unless noted otherwise, upgrading within v2.x is composer update racerfish/evolve.

v2.22.3 latest ​

2026-11-14

Patch release. Upgrading is composer update racerfish/evolve.

Fixes ​

  • Database copy: evolve:db:copy empties the destination before importing, so tables the source lacks (leftovers of earlier deploys) no longer survive the copy and the next migrate runs cleanly. If the source dump fails, the destination stays untouched (#366). See Environments.

v2.22.2 ​

2026-11-13

Patch release. Upgrading is composer update racerfish/evolve.

Fixes ​

  • Module overview links: "all posts" and the other links to a module's overview page (members, jobs, projects) use the page's stored URL instead of one built from its title, so renaming the page or editing its URL no longer breaks them (#365).

v2.22.1 ​

2026-11-13

Patch release. Upgrading is composer update racerfish/evolve.

Fixes ​

  • Migrations: the health tables migration skips an existing history table, so installs that come from before 2.x migrate cleanly (#363).

v2.22.0 ​

2026-11-13

Minor release that finishes the cut between core and modules for media and email. Form mail templates live in the core now, the email module (campaigns, Brevo contact sync) is off by default, and media get extension points plus an optional Media Governance module. Upgrading is composer update racerfish/evolve, no migration. Sites that send newsletters or sync contacts to Brevo set EVOLVE_MODULE_EMAIL=true with the update. See To 2.22.

Modules ​

  • Email module off by default: behaviour change campaigns, Brevo contact sync and the Email entry in the sidebar only run with EVOLVE_MODULE_EMAIL=true. Form mail templates, email blocks and their renderer moved into the core and work without the module, the panel entry is under the core section at /evolve/mailtemplate (#358). See Mail templates.
  • Media Governance: new optional module for approval, rights levels and IPTC prefill of media, off by default. After switching it on run php artisan migrate (#357).

New ​

  • Media extension points: hooks for media visibility, uploads, permissions and the media library screens, for modules and projects (#356). See Media extension points.

Fixes ​

  • Blocks: "make master" on a clone opens the block editor of its carrier instead of a broken URL (#359).
  • Install: evolve:install creates the .env it writes instead of a stray empty file (#360).
  • Media library: select all and bulk delete respect the active list filters (#364).

v2.21.0 ​

2026-11-08

Minor release that cleans up the module boundaries of the core. Every module now has exactly one switch, routes from projects and packages win over the page catch-all, and laravel/mcp and laravel/ai are no longer installed with Evolve. Upgrading is composer update racerfish/evolve, no migration. One thing needs attention in every project: sites that use MCP or the AI chatbot run composer require laravel/ai laravel/mcp in the same step, all other sites set EVOLVE_MODULE_MCP=false. Details under To 2.21.

Modules ​

  • MCP and AI packages: behaviour change laravel/mcp and laravel/ai are suggested instead of required. Without them MCP and the chatbot stay off, site and panel keep working, and the module switches check warns (#355). See Modules with requirements.
  • One switch per module: every module, including project modules and region, resolves to one switch. EVOLVE_MODULE_* also works for modules a published modules list lacks, and evolve:install writes all switches into the .env (#352). See Switching a module on or off and Module switches.
  • Health endpoint: reports module switches and drivers for the Fleet check (#351).

New ​

  • Block links: block carriers supply their editor link through the BlockContainer contract, so search results, activity log and media usage open the right editor for module and project models. Shopify and Scout24 register their own health checks (#353). See Global Search.
  • Chatbot: tables, nested lists, links in a new tab and a longer history (#342).

Fixes ​

  • Routing: the catch-all page route is registered after all providers booted, so project and package routes win (#350). See Route order and the catch-all.
  • Tracking: requests without referrer no longer raise a parse_url deprecation (#347).

v2.20.0 ​

2026-10-30

Minor release with one change: the environment hint on staging and development frontends is a small pill at the bottom centre instead of a full-width bar. Visitors can shrink it to a dot in the corner, the browser remembers that. Upgrading is composer update racerfish/evolve, nothing else to do; EVOLVE_ENV_BANNER=false still hides the hint per site.

New features ​

  • Environment hint as a pill at the bottom centre with a minimize button, dot in the bottom-left corner when minimized, state kept in the browser (#346).

v2.19.0 ​

2026-10-24

Minor release that takes the last Racerfish keys out of the package, adds a consent platform driver, an opt-in video driver for the Racerfish video resolver, mail templates for form mails, readable file URLs and a sortable page list. Upgrading is composer update racerfish/evolve, then php artisan migrate (settings rows for the CMP, mail template tables and columns). Three things need attention before or right after the update: set the site's own Google keys and service account, check the timezone, and on opt-in sites declare footprints as a marketing cookie. The full list, including the view copies to update, is under To 2.19.

Security ​

  • No shipped Google keys: GOOGLE_MAPS_API_KEY, GOOGLE_GEOCODING_API_KEY and GOOGLE_PAGESPEED_KEY have no package defaults any more. Without a key the Locator lists without a map and skips geocoding, and the new api_keys health check names the keys enabled modules need. Set the site's own keys in the .env before updating (#316). See Health Checks and Locator.
  • No shipped service account: the committed Google service account file is gone; the key is being rotated. The dashboard reads the GA4 Data API with the site's GOOGLE_APPLICATION_CREDENTIALS (absolute, or relative to the project root, the same file as Cloud Vision and auto-translate) and shows a notice when the file is missing. Sites that relied on the packaged account set their own before updating (#341). See Environment Variables.
  • CMP driver: Cookiebot as consent platform on Settings > Tracking (or TRACKING_CMP=cookiebot with COOKIEBOT_ID). Evolve renders the Cookiebot script and bridges its decision into the Evolve consent cookie; other platforms call window.evolveConsentUpdate(). The tracking health check warns when an opt-in site has no working bridge (#328). See Tracking and Consent and Consent in the Theme.
  • Footprints after consent: behaviour change on opt-in sites the footprints attribution cookie is set only after marketing consent. Declare it in the consent platform (#331). See Cookies Evolve sets.
  • Timezone check: a health check warns when app.timezone is UTC, because editors type Swiss local time and scheduled blocks and publication dates would take effect late. Set APP_TIMEZONE=Europe/Zurich (#327). See Health Checks.

New ​

  • Racerfish video driver: opt-in with VIDEO_DRIVER=racerfish. Stored Vimeo IDs stay in the content; the Racerfish video resolver decides per video whether Bunny Stream or Vimeo delivers it, so videos move off Vimeo without touching the site. If the resolver is unreachable, the driver plays the last good Bunny answer, then Vimeo directly, and costs a page at most one timeout per minute. The default stays vimeo (#337). See Racerfish video driver.
  • Mail templates (email module): confirmation and notification mails of a form can use a template built from blocks with placeholders, rendered through the campaign layout; forms without a template keep their Markdown mails (#338). See Mail Templates.
  • Page list: sort by title, URL or last change, filter by visibility, and a "Default order" button (#339). See The page list.
  • Readable file URLs: files are served under /media/{hash}/{slug}.{ext}, the copy link buttons use that form, the old /download URLs keep working (#336). See File URLs.
  • Email contacts in the panel: read only overview of the newest contacts of the main list and a contacts list (#298). See Email Campaigns.
  • Chatbot context: FAQ and blog post context keep links as Markdown and annotate linked media files (#334).

Fixes ​

  • Core: module Livewire components register under the module's own vendor namespace, so project modules under Acme\… no longer answer 419 on every action; their manual loader call can go (#297). Resource lists sort only by columns a component allows through sortableColumns() and applySorting(), unknown values fall back to the default order instead of a 500 (#319). Modules merge their config before the enabled flag is read, so MCP_HTTP_ENABLED works under the workbench (#322).
  • Settings: an untouched save no longer writes every missing env key with its config default into the .env; the overview page selects post an empty value for "None" (#315).
  • SEO: noindex keeps follow, canonical URLs are validated and redirect chains resolve to one hop (#283). See SEO and Head.
  • Panel: Regions is translated everywhere it appears (#321); attached files in the media picker open the library in a new tab (#330); the Shopify feed preview renders in process instead of requesting its own URL (#320); the installer no longer writes a Racerfish from address into the .env (#317).
  • Company: members and team blocks never show inactive members (#269).
  • Email campaigns: duplicated campaigns keep their visible blocks, and new campaign blocks start visible (#344).
  • Theme: the dead tile-group component is gone from the starter theme (#335).

v2.18.0 ​

2026-10-16

The settings panel now saves the public business configuration to the settings store instead of the .env: site name, branding, tracking and consent, site verifications, mail addresses and the new company identity. Secrets, infrastructure and posture stay in the .env. One behaviour change needs attention: forms without an administrator address no longer mail the sender address. Upgrading is composer update racerfish/evolve, php artisan migrate, then once per site php artisan evolve:settings:import; the steps, including the view copies to update, are under To 2.18.

Security ​

  • MCP permissions: over HTTP, write tools check the panel permissions of the token's user (pages.create, pages.delete, pages.publish, pages.edit); before, any valid token could do everything. The unused PageUpdateRequest and UserStoreRequest check pages.edit and users.manage (#288). See MCP Server.
  • laravel/mcp: Evolve allows 0.9.6 and 1.x, which fix GHSA-mx2h-h55v-pm44. Evolve does not use the affected OAuth routes; run composer update laravel/mcp -W to pick the fix up (#292).

Settings ​

  • Panel writes the store: Tracking, SEO, site and branding fields are saved to the settings store, show the inherited .env value as placeholder, and fall back to it when emptied. Site verifications moved to a new SEO tab, LinkedIn IDs got fields, switches are selects with an inherit option, and an import hint shows while a group still runs on its .env (#301). See Settings.
  • Mail addresses: sender, reply-to, notification and alert address live in the new mail group on the Mail tab. Leads of forms without an administrator address go to the notification address, or nowhere with a logged warning, never to the sender. Health failure mails follow the alert address (#290). See Mail.
  • Company tab and Organization: legal name, brand name, address, contact, profiles and logo, from which every page gets one schema.org Organization. author and publisher of articles point at it instead of a company.name translation. Modules emit their own JSON-LD through Head\JsonLd (#294). See SEO and Head.
  • Consent: opt-in cannot be saved without the cookie banner or the new "Consent via CMP" option for a consent platform in GTM. The banner switch moved to the Consent card of the Tracking tab (#296). See Tracking and Consent.

New ​

  • Activity log: entries show type labels and linked record titles instead of class names, and the filter URL uses short aliases. Modules register their types with ActivitySubject (#291). See Extending Evolve.
  • AI context: media links in page and block context carry the file's name, description, type and size, so the chatbot can name a linked PDF (#293).

Fixes ​

  • Panel: create screens name the entity, Forms, Settings and Events have their own browser tab title, leftover Nitrous copy is gone (#295).
  • Theme: the Tiles layout field switches between compact and large tiles again (#289).
  • Core: address fields are plain text inputs; the Algolia Places autocomplete, shut down by Algolia, is gone (#287).

v2.17.0 ​

2026-10-14

Form leads can now become Brevo contacts, with double opt in, a webhook for unsubscribes and bounces, and a retry queue (see Brevo and Forms and leads). Modules can register form types. Upgrading is composer update racerfish/evolve followed by php artisan migrate (new form, lead and email tables). Projects with copied panel views run php artisan evolve:view-overrides --since=2.16.1 (form.index, lead._form, components.resource.list-header, whose copy now calls sort() instead of sortBy()). Projects with their own copy of the theme's form.blade.php carry over the ?confirmed= branch to show the double opt in confirmation.

New ​

  • Email: Sync form leads to Brevo contacts with double opt in, webhook and retry queue (#282).
  • Forms: Form types registered by modules, LeadCreated event and consent snapshot on leads (#278).

Fixes ​

  • Media: Downloads no longer answer 500 when the media name contains a slash (#286).
  • Core: Sortable list headers sort again (WithSorting::sortBy() is now sort()) (#285).
  • Deploy: An undeclared default environment aborts instead of falling back to live (#284).

v2.16.1 ​

2026-10-13

Patch release for the mailer health check. Upgrading is composer update racerfish/evolve, no migration.

Fixes ​

  • Mailer health check: the daily test mail never goes to MAIL_FROM_ADDRESS any more, which on client installs is the client's own mailbox. Recipient is HEALTH_CHECK_EMAIL_RECIPIENT, fallback HEALTH_WEBMASTER_ADDRESS; without either the check only probes the transport. The transport probe (SMTP connect and auth, sendmail binary) runs hourly, the real mail goes out at most once a day (#281).

See Health Checks.

v2.16.0 ​

2026-10-11

Minor release that tightens form spam protection. Upgrading is composer update racerfish/evolve, no migration. Sites with reCAPTCHA keys now reject low-score visitors, so watch incoming enquiries after the update.

Security ​

  • reCAPTCHA v3 score: a submission needs a score of at least services.google.recaptcha.score (default 0.5). Before, every token Google accepted passed, including clear bots. Override it with GOOGLE_RECAPTCHA_SCORE or under Settings > Google. v2 tokens carry no score and still pass on success. If Google cannot be reached, the submission is rejected (#274).
  • Honeypot timing gate: it read core.honeypot-threshold, which no config defines, so only posts within the same second were caught. The new key evolve.honeypot-threshold defaults to 3 seconds (EVOLVE_HONEYPOT_THRESHOLD). Projects that set core.honeypot-threshold keep their value (#274).
  • Event registrations run the honeypot alongside reCAPTCHA, as forms already did (#274).

Details and theme integration: Form Spam Protection. Legacy 1.x gets the same checks in v1.95.0.

v2.15.0 ​

2026-10-10

Minor release with two security fixes in the panel and a conversation log for the AI chatbot. Upgrading is composer update racerfish/evolve, then php artisan migrate. Update soon: both security holes are reachable without an account.

Security ​

  • Invites: claiming an invite now requires the signed URL from the invitation mail. Before, anyone could post a name and password to a pending invite and receive its email and role. The form is throttled, and registering through an invite works again (it threw in every case) (#270).
  • Activity log: messages escape every placeholder value. A failed login with markup as email could run script in the session of the next admin opening the log (#270).

AI Chatbot ​

  • Conversation log: questions, answers, page, tool calls, model and token usage are stored per conversation (still no IP). The panel lists them under Chatbot > Unterhaltungen with search, a thread view and a CSV export (#273).
  • Retention: CHATBOT_RETENTION_DAYS (default 90, 0 keeps everything). The module schedules model:prune daily, so the host needs a running Laravel scheduler (#273).
  • Notice: block and widget show CHATBOT_NOTICE below the input (:days placeholder). Mention the log in the site's privacy policy (#273).
  • Projects with a published chatbot view should send conversation along to get grouped threads (#273).

Panel ​

  • Page list: the visibility icon sits on a coloured badge again (green public, blue hidden, red private) (#272).

v2.14.0 ​

2026-10-06

Minor release with a floating chat widget for the AI chatbot and a server side system prompt. Upgrading is composer update racerfish/evolve, then php artisan migrate.

Security ​

  • System prompt: the chatbot reads the system prompt from the published cms.aiChatbot block on the server. A prompt sent by the client is ignored, so visitors can no longer replace the instructions (#265).
  • No IP storage: a migration drops evolve_chatbot_questions.ip_address. The rate limiter keys on the IP in cache only (#265).
  • Escaped output: model output is HTML escaped before Markdown rendering, links only to http(s), relative and mailto targets (#265).

AI Chatbot ​

  • Conversation history: follow up questions send the earlier turns along (ai-chatbot.history_limit, default 10) (#265).
  • Tools and agent class: ai-chatbot.tools registers tool classes, ai-chatbot.agent swaps in a subclass of ChatbotAgent (#265).
  • Floating widget: ai-chatbot::widget adds a chat button in the bottom right corner on every page, with content from a published cms.aiChatbot block (CHATBOT_WIDGET_BLOCK picks one by ID). Off by default, enable with CHATBOT_WIDGET=true. Block and widget pass the current page to the agent, so "this product" resolves (#266).
  • Starter theme: frontend.blade.php includes the widget. Existing projects add the include to their layout and rebuild the theme CSS (#266).

Upgrade steps: Upgrading to 2.14.

v2.13.0 ​

2026-10-04

Minor release with a redesigned control panel: monochrome in light and dark mode, one sticky action bar per screen, records in a side drawer, Cmd+K for screens and actions, slim list rows and Nucleo icons instead of Font Awesome. The panel loads about 470 KB instead of 2.3 MB per page. Nothing changes on the website. Upgrading is composer update racerfish/evolve plus the upgrade steps linked below. The full list of changes is in Upgrading: Panel Refresh.

Panel ​

  • Smaller and faster: split bundle, charts, editor, tree, selects and date pickers load on demand, system fonts, icons from one cached SVG sprite (#251, #254).
  • Monochrome design: primary is the ink of the mode, the brand colour (evolve.theme-color) only colours the logo and loading bars, new Evolve signet, sidebar for content, system screens in the user menu (#255).
  • Action bar: breadcrumb, title, actions and saving in one sticky bar per screen, with an unsaved changes guard. Save & close only redirects within the panel's own host (#256).
  • Side drawer for records edited from a list, including media, users and invitations (#259, #267).
  • Cmd+K finds screens, actions and content, filtered by the user's roles. Modules register their own commands (#263).
  • Slim list rows and row menus that no longer inline their script: the demo page list went from 305 KB to 122 KB of HTML (#264).
  • One solid button per view, segmented controls for filters and view modes, translated health screen (#267).
  • evolve:view-overrides lists project copies of Evolve views and the changes they must follow (#253).

Other ​

  • Video picker: parents can reset the picker to list mode with a video-picker.reset event (#250).

Upgrade steps: Upgrading to 2.13.

v2.12.0 ​

2026-09-26

Minor release that hardens how a site runs on its server. Uploads move into one shared directory per environment, so a deploy can no longer delete them. Image variants render in a queue worker instead of in the visitor's request. The page cache stops writing pages that were rendered for a logged-in user. Every response carries baseline security headers. Public business settings (tracking IDs, verification codes, site name, branding, company identity) get a database store that the config reads from, and Shopify products carry their real status per store. Upgrading is composer update racerfish/evolve plus the upgrade steps linked below, once per site.

Security ​

  • Page cache: a page is never written when the request is authenticated (panel login, remember-me, a login of any guard), when the response contains the info bar, or when it sets a cookie of its own (#236).
  • Security headers: nosniff, X-Frame-Options: SAMEORIGIN and Referrer-Policy on every web response, HSTS on live over https, CSP opt-in. Configured under evolve.security-headers (#224).
  • Cache actions are POST only. The panel's clear, flush and rebuild actions no longer run on GET; an old GET link lands on the cache overview without doing anything. The info bar's cache icon opens the overview (#224).
  • Less data in logs: Shopify order and inventory webhooks log identifiers only, the abandoned checkout and back in stock jobs log ids instead of email addresses (#224).
  • Server-side fetches: Util::toInlineStyles() only fetches https URLs whose host is listed in evolve.inline-styles-hosts (default fonts.googleapis.com). The store locator geocoder rejects empty or overlong input, times out after 5 seconds and caches under a namespaced key, so every address is geocoded once more after the update (#224).
  • Login links without an expiry are rejected; media text previews are read from disk instead of over HTTP; Visit is no longer fully mass assignable (#227).

New ​

  • Shared storage per environment. storage/app lives in shared/storage/app, every release links to it, the purge skips any release that still carries its own copy. See Deployment (#203).
  • Queued image conversions. Uploads queue the variants <x-image> requests, listed once in image.conversions. A cold variant is answered with the original and rendered by the worker. New command evolve:images:warm {--missing}. See Image Rendering (#240).
  • Settings store. TrackingSettings, SeoSettings, SiteSettings, BrandingSettings and CompanySettings in the table evolve_settings, overlaid onto the config. New command evolve:settings:import {--dry-run} {--force} {--include-defaults} {--group=}. The health check's tracking block reports a sources field. See Settings Store (#239, #241).
  • Role access per area. evolve.roles sets which roles reach the media library, the AI editor and each module; the sidebar hides what a user cannot open. See Roles (#208).
  • Permission catalogue. Permissions such as pages.edit or modules.blog.access are created and granted to the built-in roles. Access is still decided by role names in 2.12. See Roles (#242).
  • Sentry context. site and environment_type tags and an Evolve release on every event, plus the Sentry DSN line in evolve:env:verify. See Error Tracking (#221).
  • Shopify product status per store (published, unpublished, draft, archived, deleted) from the Admin API. A product unpublished on a mirror is no longer offered there. The product manager shows why a product is not published on the selected store. See Shopify (#238).
  • Shopify purge. shopify:purge-deleted-products {--days=} {--dry-run} deletes products Shopify confirmed as deleted, after a retention window; SHOPIFY_PURGE_DELETED_AFTER_DAYS schedules it daily. Store data, translations and collection links cascade. The products/delete webhook of a mirror only removes that store's data (#243).
  • AI block generator: generated cards and tiles land inside a cms.cards or cms.tiles container instead of rendering as missing blocks (#234).

Removed ​

  • Racerfish\Evolve\Repositories\ImageRepository (use Racerfish\Evolve\Services\ImageRenderer) and Media::updateEdited(). Nothing in the package or the published files referenced them (#234).

Fixes ​

  • Page cache: the import forgets the cached page of every product it archives, so an archived product answers 404 instead of its cached page (#243).
  • Settings: saving rebuilds the config cache in a separate process instead of clearing it, so a deployed site stays cached. If the rebuild fails, the cache is cleared, the reason is logged and the panel shows an error (#222). The queue health check is a select with Auto, On and Off; Auto removes HEALTH_CHECK_QUEUE from the .env again (#225). See Settings Store.
  • Images: URLs under /index.php/img/... answer 301 to /img/... instead of failing to cache (#223).
  • Panel: block editor, navigation tree, content import, lead modal, forms and the magic link no longer crash or half-save on missing data (#226). Block prop views take placeholders, the auto-translate button and the "Default" option from the page region's fallback locale (#220).
  • Theme: tiles on the default "auto" width share the row; hero, logo cloud, multi-column text, tab panels, Shopify products and the blog composer render with empty fields; cart-store.js stays quiet without a store; oEmbed failures no longer throw (#228).
  • Events: cache listeners on Language and Redirect, and the login activity listeners, no longer stop later listeners of the host app (#219).

Upgrade steps: Upgrading to 2.12.

Patches ​

  • v2.12.1 (2026-09-26)
    • Patch release. Upgrading is composer update racerfish/evolve. After the update, run php artisan evolve:clear-images once on sites with ICC tagged images, so their WebP derivatives are rendered again at the configured quality.
    • Images: WebP derivatives of images with an embedded ICC profile use the configured quality (image.compression.webp.quality) instead of the encoder default, which produced files several times larger than needed (#235).
    • Settings store: evolve:db:copy and the Envoy tasks database:down and database:up forget the settings cache on the side that received the database, so the site shows the copied settings right away. After a manual SQL edit, run php artisan cache:forget evolve.settings.v1. See Settings Store (#245).

v2.11.0 ​

2026-09-25

Stabilisation release. Tracking now follows the environment type, the health check reports why a live site stopped measuring, and a batch of region, Blade and panel fixes from the v1 migrations lands. Upgrading is composer update racerfish/evolve, then check two things.

Staging sites stop sending tracking. Only live emits GTM, GA4, Hotjar and the server side conversions now. A staging site that is meant to fire the tags for a test run needs TRACKING_EMIT=true, a live site that must stay silent TRACKING_EMIT=false. See Environment Variables.

Check the environment type of every live site. The type falls back to APP_ENV, and only production means live. A live server with neither EVOLVE_ENV_TYPE=live nor APP_ENV=production now emits no tracking at all, and the tracking health check reports "Not live".

Projects that published the starter theme keep their own appbar.blade.php and do not get the fix from #188; copy the three @if guards into the override.

New ​

  • Tracking by environment type. TRACKING_EMIT overrides it in both directions; the developer can set it in the settings panel. The tracking block of /api/health-check now reports the type, whether the site emits, every public ID (GTM, GA4, Meta, Pinterest, LinkedIn, Hotjar), the consent policy and whether the API secrets are set (#197).
  • services config merges recursively. A project config/services.php that defines services.google no longer wipes the package keys it leaves out (GA4 id, Maps key, reCAPTCHA). Values a project config empties are reported by the new ShadowedConfigCheck, live tracking IDs by TrackingCheck. Toggles: HEALTH_CHECK_SHADOWED_CONFIG, HEALTH_CHECK_TRACKING. See Health Checks (#191, #201).
  • Region pages in the panel. The editor shows the locales of the page's own region, and assigning a region copies the existing content into the region's fallback locale without overwriting anything. Pages assigned before this version: php artisan evolve:seed-region-content {--page=*} {--dry-run} (#207, #204, #205).

Fixes ​

  • Panel: a stale Livewire 2 bundle in public/vendor/livewire is republished on the next console boot instead of silently disabling Alpine; evolve:deploy refuses to sync one and evolve:env:verify reports it (#190). FAQ and campaign lists page past page one (#209).
  • Static strings: a string maintained only in the fallback locale resolves on every language in Blade too (#189). The app bar hides contact links whose string is not set, instead of linking to company.google-maps-url (#188).
  • Blocks: region overrides on repeatable fields reach views as objects (#195); a cms.form block that references its form by bare id renders instead of answering 500 (#200).
  • Core: the Evolve alias the starter theme calls is registered (#198); exceptions with solutions no longer fatal without Ignition, two classes that could not load are removed (#199).
  • Deploy: a failed release probe aborts instead of building a release without uploads, and the new release is copied from the live one rather than the newest by name (#194).

v2.10.0 ​

2026-09-10

Minor release for SEO, the document head and the page cache. Upgrading is composer update racerfish/evolve, then check two things on every live site. APP_URL must name the real domain: the page cache now only writes for that host, and the new canonical host health check fails when the cache is on and APP_URL still points at localhost or a .test domain. /.well-known/security.txt no longer falls back to a Racerfish address: set evolve.security.contact, or a real mail from address, otherwise a live site answers 404 there.

Security ​

  • Page cache: entries are only written for the canonical host from APP_URL. A request on a preview host, or with a forged Host header, used to write pages whose asset URLs pointed at that host, and those were then served to everybody. A site without a usable APP_URL writes nothing. See Page Cache (#182).
  • Head tags: attribute values and text content are escaped (#184).
  • security.txt: the customer's own contact instead of a Racerfish address. evolve.security.contact wins, then the mail from address once it is no longer the seeded one, otherwise 404 on live (#183).

New ​

  • Canonical host health check. Passes when APP_URL names a real host, warns when it does not, fails when it does not while the page cache is enabled. Toggle: HEALTH_CHECK_CANONICAL_HOST. See Health Checks (#182).
  • x-default alternate next to the per locale alternates, pointing at the fallback locale of the region or the app (#186).

Fixes ​

  • Meta description: an empty description no longer falls back to the app name, which had given most pages of a site the same one word description. Without one, no tag is emitted and search engines write their own snippet (#186).
  • Sitemap: URLs are built from the site URL instead of the requesting host, and carry hreflang alternates (#185).
  • Structured data: valid @context, so the JSON-LD block is read at all; the page cache writes into the document root where the web server looks for it; the locale switch falls back correctly (#181).
  • Navigation cache: saving a navigation forgets the tree of every region, and a missing item no longer breaks rendering (#164).
  • Mail: the "Sent by Evolve" line is gone from the footer (#165).
  • AI chatbot: a provider error inside the stream shows the unavailable notice instead of an empty answer (#180).

v2.9.0 ​

2026-09-06

Minor release for the agency fleet tooling. The health check endpoint reports editorial activity, and the MCP server can publish or unpublish a page. Upgrading is composer update racerfish/evolve. Themes based on the starter should copy theme/views/components/blocks/cms/video.blade.php from the package: the old view read a field the published Video blueprint never defined, so hosted videos did not render.

New ​

  • Editorial activity in the health check. GET /api/health-check carries a content block: last content change (when, by whom, on what), change counts over 30 and 90 days, distinct editors over 90 days, and the last successful panel login. Read from the activity log, no migration. The block is always present; without data every value is null. See Health Checks (#168).
  • MCP: publish state. set-page-published-tool publishes or unpublishes an existing page. Reversible, so no confirmation flag; available to authenticated callers only. See MCP Server (#168).

Fixes ​

  • Theme: the video block reads videoVideo, the field of the published blueprint, keeps the video's real aspect ratio and only renders the embed fallback when a URL is set (#169).
  • Email: the duotone icons the email blueprints use are registered (#167).

Patches ​

  • v2.9.1 (2026-09-06)
    • Patch for the health check's content block. Evolve models log custom descriptions, so v2.9.0 counted no content changes at all; changes are now counted by the activity log's event column. Upgrading is composer update racerfish/evolve.
    • Health check: content changes are counted by the activity log event column, so changes_30d, changes_90d, editors_90d and the last change report real editorial activity (#170).

v2.8.0 ​

2026-09-05

Minor release: the environment model settles on three types, and the developer role migration and the email module icon get their fixes. Projects on ^2.8@RC move to ^2.8.

Changed ​

  • Three environment types. development, staging, live. Testing merges into staging: every remote test site starts safe (mail redirected, noindex, preview password as soon as one is set, debug on, banner) and a developer switches mail to real recipients per site in the Developers tab. testing in declarations and .env files is still read as staging. Database copies into staging confirm with a plain yes/no. See Environments.

Fixes ​

  • Developer role migration copes with a roles table a project extended (a NOT NULL display name column no longer aborts the upgrade).
  • Email module shows its sidebar icon.
  • Media: file extensions are stored lowercased, so uploads with uppercase extensions keep their thumbnails.

v2.7.0 ​

2026-08-30

Minor release: environments get a clear meaning per type, a developer role above admin, and the technical settings move into the panel for developers.

Breaking ​

  • Staging sends real mail. Staging is the dress rehearsal for live and behaves like it, mail included; the preview password keeps the public out. Every install with EVOLVE_ENV_TYPE=staging should have EVOLVE_PROTECT_PASSWORD set (evolve:env:verify warns otherwise) or EVOLVE_MAIL_SAFETY=true until it is ready. Testing sites keep redirecting mail.
  • Devtools require the developer role. The migration creates the role and grants it to racerfish.com logins; promote others from the user screen.

New ​

  • Environment types: development (debug, mail as configured, banner), testing (mail redirected, banner, preview password), staging (like live with debugging, real mail, banner, password expected), live. See Environments.
  • Frontend banner on every non-live environment, injected by the package, no theme change needed. EVOLVE_ENV_BANNER=false hides it.
  • EVOLVE_MAIL_SAFETY overrides the type in both directions; EVOLVE_MODULE_<NAME> switches modules from the .env.
  • Developer role: everything an admin can, plus the Developers tab (environment type, debug, banner, mail safety, preview protection, module switches) and the devtools. Only developers assign it. See Roles.
  • Developers can declare a development environment as a remote (early alpha on a server).

Fixes ​

  • Roles seeder tolerates roles that already exist.

v2.6.0 ​

2026-08-28

Minor release, the largest since 2.0: deployment environments with policies, region based content, a campaign email module, server side conversions and consent mode, and a long QA pass on the demo site. Upgrade with composer update racerfish/evolve, then sync the theme files listed under "Theme" and rebuild.

New ​

  • Environments. Projects declare live, staging and testing environments with a type driven policy: typed confirmation for live, database pushes never into live, mail redirected and preview protection on staging and testing. Commands evolve:env:list, evolve:env:setup, evolve:env:verify, evolve:env:diff, evolve:db:copy, evolve:ssh. See Environments and Mail.
  • Regions. Content can be overridden per region (country) without duplicating pages; region prefixed URLs such as /ch-de/ resolve to the same page. Shopify stores map to regions.
  • Email campaigns. Module with email layout blocks, HTML export and Brevo push.
  • Tracking. Google Consent Mode v2 with a Swiss opt out default, server side conversions through the GA4 Measurement Protocol and Meta CAPI, Shopify purchase events. Legacy globals sit behind a deprecation flag.
  • Demo site. php artisan evolve:demo seeds the Kitchen Sink page and demo content for every module into any install.
  • Media index for blueprint fields and inline links; read only remote inspection commands; health checks streamlined around Oh Dear.

Fixes ​

  • Frontend: the theme now actually loads Livewire. Since 2.1 the layout declared bundled assets while app.ts only bundled Alpine, so Livewire blocks (blog list, store locator) fell back to full page loads. wire:loading elements start hidden.
  • Module blocks render from their module without a vendor:publish; a block without any renderer leaves an evolve:missing-block comment in production instead of vanishing.
  • Event module: the CMS page with slug events wins over the module route; detail pages are routeable by default; ICS download link on the detail page; list sorted and filtered correctly in the theme copy.
  • Settings: only known keys are written, secrets are redacted in the activity log, dead toggles removed; the CAPI key no longer appears in the HTML.
  • Form field names with spaces or punctuation can be submitted again.
  • Selects show one arrow; x-track payloads on card, tile and product buttons are JSON and reach tracking.
  • Seed commands pass --force through so production guards cannot cancel them silently.
  • Panel: sortable columns reset direction correctly, media picker keeps folders and search state, locales for FR, IT and NL selectable, REST resource endpoints repaired, Livewire pages excluded from the page cache, missing indexes added.

Theme ​

Projects on the starter theme sync these files from theme/theme in the package and rebuild: vite.config.mts, src/app.ts, src/lib/alpine.ts, src/livewire.d.ts, src/styles/form.css, views/frontend.blade.php, views/components/common/navbar.blade.php, views/components/map.blade.php, views/components/ui/tile.blade.php, views/components/ui/card/footer.blade.php. The JS bundle grows by roughly 130 kB because Livewire is now part of it.

Patches ​

  • v2.6.1 (2026-08-29)
    • Patch release.
    • Saving a user in the panel assigns the chosen role again.
  • v2.6.2 (2026-08-29)
    • Patch release.
    • Settings: saved values show immediately (the screen redirects after a changed save instead of re-rendering stale config).
    • Settings: the environment is shown read only in the Developers tab and is no longer editable. The former select did not know staging, so a save on a test site wrote APP_ENV=local. See Environments.
    • Health: a new environment consistency check fails when APP_ENV and EVOLVE_ENV_TYPE contradict each other and warns on a server without a declared type.

v2.5.0 ​

2026-07-19

Minor release: the package becomes its own development environment, plus locale, sitemap, and deployment hardening fixes. First release cut through the automated release flow (semver labels, drafted notes, tag on publish).

New ​

  • Standalone development workbench. vendor/bin/testbench serve boots a full demo installation from the package repo alone: admin panel, a Kitchen Sink page covering every block blueprint, demo content for all modules, and module overview pages. See Local Development. Blueprint discovery now supports multiple paths per namespace.

Fixes ​

  • Blocks: empty repeatable fields fall back to the fallback locale on the frontend (empty lists no longer render blank in secondary languages).
  • Sitemap: noindex routes excluded, lastmod derives from real content changes, placeholder priority/changefreq dropped.
  • Deployment: destructive Envoy tasks (database:up, media:up, env:up) require target-name confirmation and write timestamped safety backups first.
  • Portability: MySQL-only FIELD() and REGEXP usage removed, so the package runs on sqlite and Postgres.
  • module_index_url() no longer fatals without a locale or a configured overview page.
  • Module list views (blog, company, events, FAQ, projects, locator) ported to the current card component API; they rendered without titles, images, and buttons before.
  • Redirects honor is_active; tagless saves no longer wipe tags; refreshed prebuilt panel assets.

v2.4.0 ​

2026-06-23

Minor release. Adds a cart-store extension API and ships the first batch of security-audit fixes.

New ​

  • Shopify cart post-mutation hook API. Alpine.store('cart').addPostMutationHook(fn) runs after every successful cart mutation (add, update, remove), so projects extend cart behaviour instead of forking the store. Store registration is now idempotent.

Security ​

  • Shopify webhook no longer logs the shared secret or the raw request body on HMAC mismatch.
  • Locator block escapes CMS-editable fields before unescaped output (stored XSS).
  • MediaManager gains a config-driven upload allowlist (evolve.media-manager.allowed-extensions) that blocks active-content types while keeping documents like PDF.
  • FaviconController hardened against SSRF and arbitrary public-file writes.

Dependencies ​

  • laravel/mcp widened to ^0.6 || ^0.7 || ^0.8.

Patches ​

  • v2.4.1 (2026-06-29)
    • Bugfixes for dangling resource references (Field::resource(...) values whose target row no longer exists):
    • Block::hydrateProps() now nulls a reference whose target row is missing instead of leaving the raw reference array, which broke consumers expecting a model or null.
    • A single resource picker now replaces its selection on pick instead of appending, which previously kept a stale id in place and silently discarded the new pick.
    • The theme form block scaffold only renders the form component when a form is present.
  • v2.4.2 (2026-06-30)
    • Block editor no longer bleeds translations across locales (#32). Translatable block fields stay independent per language. Previously a value entered for one locale could leak into others and be persisted across all languages on save, because the field container was not keyed by the active locale and Livewire's DOM morphing reused stale input nodes when switching language tabs.
  • v2.4.3 (2026-06-30)
    • Bugfixes for the Shopify collection block:
    • Guard optional productType in the product card and list, so products without a product type no longer break the collection and products block (#33).
    • Add an empty option to the collection select so a nullable category can be saved (#34).

v2.3.0 ​

2026-06-21

Features ​

  • MCP remote (HTTP) transport with automatic tiered access. Anonymous callers get read-only published content, token-bearing callers get the full tool set (#20).
  • schema.org JobPosting JSON-LD plus structured job fields (#7).
  • Shopify GraphQL Admin API behind a client interface, replacing the dead Admin client (#19).

Fixes ​

  • Media: HTTP Range request support (#14), consistent attachment counts, and orphaned pivot cleanup (#15).
  • Dotenv service no longer corrupts .env structure on writes (#16).
  • Theme: replaced the custom Vite integration with laravel-vite-plugin (#13).
  • Analytics dashboard: graceful failures, stale-wiring cleanup (#12), and a Livewire 3 rendering fix (#26).
  • Routes: fixed locale-prefix stacking on routeable collection saves (#11).
  • Blocks: tolerate unresolvable resource values in hydrateProps (#17).
  • Navigation: tolerate a routeable with no route (#23) and a missing main navigation (#25).

Patches ​

  • v2.3.1 (2026-06-21)
    • MCP: allow laravel/mcp ^0.7 and ^0.8 (#28).
    • MCP: test coverage for the Sanctum bearer-token auth path (#27).

v2.2.0 ​

2026-05-06

New ​

  • Health check cleanup (#3): config toggles per check, results DB pruning, a Health Checks settings tab with Oh Dear configuration, and native wire:confirm dialogs.
  • Publishable Claude Code starter templates (#4): CLAUDE.md, blueprint skill, and MCP config for greenfield projects.

Fixes ​

  • Theme: guard the Shopify cart on the module toggle instead of the config domain, and defer Shopify component resolution to runtime (#2).
  • PHP 8.4 compatibility fixes.
  • Company module integration cleanup (#5).

Patches ​

  • v2.2.1 (2026-05-08)
    • Backported the remaining v1 fixes from the pre-monorepo package repos (#6).
  • v2.2.2 (2026-05-22)
    • MCP module enabled by default and laravel/mcp bundled as a package dependency (#8).
  • v2.2.3 (2026-05-24)
    • MCP: New write tools: add-block-to-page, add-child-block, delete-block, delete-page (#9).
  • v2.2.4 (2026-05-26)
    • Completed the incomplete Nitrous to Racerfish\Evolve namespace migration (#10).
  • v2.2.5 (2026-05-27)
    • Initialise nested fields for inline-array repeatable fieldsets.
  • v2.2.6 (2026-06-05)
    • Analytics dashboard now wraps its view in an unconditional root element. A conditional root broke Livewire 3 root detection and left the dashboard stuck on its loading skeleton.

v2.1.0 ​

2026-03-29

Minor release.

  • Module toggle system. Modules can be enabled and disabled per installation.
  • MCP server module. Exposes CMS content to AI agents over the Model Context Protocol.
  • AI Chatbot module. Built on laravel/ai ^0.4, which requires Laravel 12.

Patches ​

  • v2.1.1 (2026-04-09)
    • Ensure route path uniqueness on update and improve path regeneration.
  • v2.1.2 (2026-04-21)
    • Fixed the deploy_exclude.txt fallback path for the monorepo layout.

v2.0.0 ​

2026-03-22

Major release combining three changes. Published under the git tag v2.0.

Monorepo ​

All 11 packages (core, blog, company, devtools, event, faq, locator, project, scout24, shopify, theme) consolidated into a single racerfish/evolve package. One composer require, one version, one update command.

Livewire 3 Migration ​

Full migration from Livewire 2 to 3 across all packages:

  • $this->emit() replaced with $this->dispatch() using named parameters
  • wire:model.defer replaced with wire:model (deferred by default in LW3)
  • wire:submit.prevent replaced with wire:submit
  • @entangle().defer replaced with @entangle()
  • #[Url] and #[Computed] attributes replace $queryString and computed property methods
  • legacy_model_binding => true required in config/livewire.php

Laravel 11 ​

Framework upgrade from Laravel 10 to 11:

  • laravel/framework ^11.0, nunomaduro/collision ^8.0, nunomaduro/larastan ^3.0
  • Carbon 3: formatLocalized() replaced with isoFormat() and ICU date format strings
  • spatie/laravel-permission ^6.0, spatie/laravel-query-builder ^6.0
  • livewire:assets publish script removed from post-autoload-dump

Breaking Changes ​

  • Requires PHP 8.3+
  • Requires legacy_model_binding => true in config/livewire.php
  • Published Livewire components must update namespace from App\Http\Livewire to App\Livewire
  • Theme layouts must replace @livewireStyles / @livewireScripts with FrontendAssets calls
  • slince/shopify-api-php removed (abandoned)
  • Date format strings in lang files changed from strftime (%d. %B %Y) to ICU (DD. MMMM YYYY)

See the full upgrade guide for step-by-step instructions.

Patches ​

  • v2.0.1 (2026-03-22)
    • Fixed spatie/laravel-permission v6 middleware namespace (Middlewares to Middleware) causing BindingResolutionException for RoleMiddleware.
  • v2.0.2 (2026-03-28)
    • Fixed Livewire 3 backend layouts. @livewireScriptConfig was incorrectly used as a replacement for @livewireScripts, but it only outputs config JSON and suppresses auto-injection of livewire.js (which bundles Alpine.js). Replaced with FrontendAssets::scripts() and FrontendAssets::styles() calls in all three backend layouts (app.blade.php, blank.blade.php, guest.blade.php).

v1.99 ​

2026-03-22

Last pre-Livewire 3 release. Monorepo structure with all packages consolidated, but running Livewire 2 and Laravel 10. Available for clients that cannot upgrade their frontend to LW3.

Install: composer require racerfish/evolve:v1.99