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:copyempties the destination before importing, so tables the source lacks (leftovers of earlier deploys) no longer survive the copy and the nextmigrateruns 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:installcreates the.envit 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/mcpandlaravel/aiare 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 publishedmoduleslist lacks, andevolve:installwrites 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
BlockContainercontract, 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_urldeprecation (#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_KEYandGOOGLE_PAGESPEED_KEYhave no package defaults any more. Without a key the Locator lists without a map and skips geocoding, and the newapi_keyshealth check names the keys enabled modules need. Set the site's own keys in the.envbefore 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.
Tracking and consent
- CMP driver: Cookiebot as consent platform on Settings > Tracking (or
TRACKING_CMP=cookiebotwithCOOKIEBOT_ID). Evolve renders the Cookiebot script and bridges its decision into the Evolve consent cookie; other platforms callwindow.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
footprintsattribution 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.timezoneisUTC, because editors type Swiss local time and scheduled blocks and publication dates would take effect late. SetAPP_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 staysvimeo(#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/downloadURLs 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 throughsortableColumns()andapplySorting(), unknown values fall back to the default order instead of a 500 (#319). Modules merge their config before the enabled flag is read, soMCP_HTTP_ENABLEDworks 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:
noindexkeepsfollow, 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-groupcomponent 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 unusedPageUpdateRequestandUserStoreRequestcheckpages.editandusers.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 -Wto 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
.envvalue 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
mailgroup 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.authorandpublisherof articles point at it instead of acompany.nametranslation. Modules emit their own JSON-LD throughHead\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_ADDRESSany more, which on client installs is the client's own mailbox. Recipient isHEALTH_CHECK_EMAIL_RECIPIENT, fallbackHEALTH_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 withGOOGLE_RECAPTCHA_SCOREor 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 keyevolve.honeypot-thresholddefaults to 3 seconds (EVOLVE_HONEYPOT_THRESHOLD). Projects that setcore.honeypot-thresholdkeep 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 schedulesmodel:prunedaily, so the host needs a running Laravel scheduler (#273). - Notice: block and widget show
CHATBOT_NOTICEbelow the input (:daysplaceholder). Mention the log in the site's privacy policy (#273). - Projects with a published chatbot view should send
conversationalong 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.aiChatbotblock 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.toolsregisters tool classes,ai-chatbot.agentswaps in a subclass ofChatbotAgent(#265). - Floating widget:
ai-chatbot::widgetadds a chat button in the bottom right corner on every page, with content from a publishedcms.aiChatbotblock (CHATBOT_WIDGET_BLOCKpicks one by ID). Off by default, enable withCHATBOT_WIDGET=true. Block and widget pass the current page to the agent, so "this product" resolves (#266). - Starter theme:
frontend.blade.phpincludes 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-overrideslists 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.resetevent (#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: SAMEORIGINandReferrer-Policyon every web response, HSTS on live over https, CSP opt-in. Configured underevolve.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 inevolve.inline-styles-hosts(defaultfonts.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;
Visitis no longer fully mass assignable (#227).
New
- Shared storage per environment.
storage/applives inshared/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 inimage.conversions. A cold variant is answered with the original and rendered by the worker. New commandevolve:images:warm {--missing}. See Image Rendering (#240). - Settings store.
TrackingSettings,SeoSettings,SiteSettings,BrandingSettingsandCompanySettingsin the tableevolve_settings, overlaid onto the config. New commandevolve:settings:import {--dry-run} {--force} {--include-defaults} {--group=}. The health check'strackingblock reports asourcesfield. See Settings Store (#239, #241). - Role access per area.
evolve.rolessets 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.editormodules.blog.accessare created and granted to the built-in roles. Access is still decided by role names in 2.12. See Roles (#242). - Sentry context.
siteandenvironment_typetags and an Evolve release on every event, plus theSentry DSNline inevolve: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_DAYSschedules it daily. Store data, translations and collection links cascade. Theproducts/deletewebhook of a mirror only removes that store's data (#243). - AI block generator: generated cards and tiles land inside a
cms.cardsorcms.tilescontainer instead of rendering as missing blocks (#234).
Removed
Racerfish\Evolve\Repositories\ImageRepository(useRacerfish\Evolve\Services\ImageRenderer) andMedia::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_QUEUEfrom the.envagain (#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.jsstays quiet without a store; oEmbed failures no longer throw (#228). - Events: cache listeners on
LanguageandRedirect, 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, runphp artisan evolve:clear-imagesonce 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:copyand the Envoy tasksdatabase:downanddatabase:upforget the settings cache on the side that received the database, so the site shows the copied settings right away. After a manual SQL edit, runphp artisan cache:forget evolve.settings.v1. See Settings Store (#245).
- Patch release. Upgrading is
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_EMIToverrides it in both directions; the developer can set it in the settings panel. Thetrackingblock of/api/health-checknow 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). servicesconfig merges recursively. A projectconfig/services.phpthat definesservices.googleno longer wipes the package keys it leaves out (GA4 id, Maps key, reCAPTCHA). Values a project config empties are reported by the newShadowedConfigCheck, live tracking IDs byTrackingCheck. 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/livewireis republished on the next console boot instead of silently disabling Alpine;evolve:deployrefuses to sync one andevolve:env:verifyreports 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.formblock that references its form by bare id renders instead of answering 500 (#200). - Core: the
Evolvealias 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 forgedHostheader, used to write pages whose asset URLs pointed at that host, and those were then served to everybody. A site without a usableAPP_URLwrites 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.contactwins, 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_URLnames 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-defaultalternate 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-checkcarries acontentblock: 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 isnull. See Health Checks (#168). - MCP: publish state.
set-page-published-toolpublishes 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
contentblock. 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 iscomposer update racerfish/evolve. - Health check: content changes are counted by the activity log
eventcolumn, sochanges_30d,changes_90d,editors_90dand the last change report real editorial activity (#170).
- Patch for the health check's
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.testingin declarations and.envfiles is still read as staging. Database copies into staging confirm with a plain yes/no. See Environments.
Fixes
- Developer role migration copes with a
rolestable 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=stagingshould haveEVOLVE_PROTECT_PASSWORDset (evolve:env:verifywarns otherwise) orEVOLVE_MAIL_SAFETY=trueuntil it is ready. Testing sites keep redirecting mail. - Devtools require the developer role. The migration creates the role and grants it to
racerfish.comlogins; 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=falsehides it. EVOLVE_MAIL_SAFETYoverrides 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:demoseeds 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.tsonly bundled Alpine, so Livewire blocks (blog list, store locator) fell back to full page loads.wire:loadingelements start hidden. - Module blocks render from their module without a
vendor:publish; a block without any renderer leaves anevolve:missing-blockcomment in production instead of vanishing. - Event module: the CMS page with slug
eventswins 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-trackpayloads on card, tile and product buttons are JSON and reach tracking. - Seed commands pass
--forcethrough 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 wroteAPP_ENV=local. See Environments. - Health: a new environment consistency check fails when
APP_ENVandEVOLVE_ENV_TYPEcontradict 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 serveboots 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,
lastmodderives from real content changes, placeholderpriority/changefreqdropped. - Deployment: destructive Envoy tasks (
database:up,media:up,env:up) require target-name confirmation and write timestamped safety backups first. - Portability: MySQL-only
FIELD()andREGEXPusage 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/mcpwidened 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.
- Bugfixes for dangling resource references (
- 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
productTypein 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
.envstructure 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
mainnavigation (#25).
Patches
- v2.3.1 (2026-06-21)
- MCP: allow
laravel/mcp^0.7and^0.8(#28). - MCP: test coverage for the Sanctum bearer-token auth path (#27).
- MCP: allow
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:confirmdialogs. - 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/mcpbundled as a package dependency (#8).
- MCP module enabled by default and
- v2.2.3 (2026-05-24)
- MCP: New write tools:
add-block-to-page,add-child-block,delete-block,delete-page(#9).
- MCP: New write tools:
- v2.2.4 (2026-05-26)
- Completed the incomplete
NitroustoRacerfish\Evolvenamespace migration (#10).
- Completed the incomplete
- 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.txtfallback path for the monorepo layout.
- Fixed the
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 parameterswire:model.deferreplaced withwire:model(deferred by default in LW3)wire:submit.preventreplaced withwire:submit@entangle().deferreplaced with@entangle()#[Url]and#[Computed]attributes replace$queryStringand computed property methodslegacy_model_binding => truerequired inconfig/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 withisoFormat()and ICU date format strings spatie/laravel-permission ^6.0,spatie/laravel-query-builder ^6.0livewire:assetspublish script removed frompost-autoload-dump
Breaking Changes
- Requires PHP 8.3+
- Requires
legacy_model_binding => trueinconfig/livewire.php - Published Livewire components must update namespace from
App\Http\LivewiretoApp\Livewire - Theme layouts must replace
@livewireStyles/@livewireScriptswithFrontendAssetscalls slince/shopify-api-phpremoved (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-permissionv6 middleware namespace (MiddlewarestoMiddleware) causingBindingResolutionExceptionforRoleMiddleware.
- Fixed
- v2.0.2 (2026-03-28)
- Fixed Livewire 3 backend layouts.
@livewireScriptConfigwas incorrectly used as a replacement for@livewireScripts, but it only outputs config JSON and suppresses auto-injection oflivewire.js(which bundles Alpine.js). Replaced withFrontendAssets::scripts()andFrontendAssets::styles()calls in all three backend layouts (app.blade.php,blank.blade.php,guest.blade.php).
- Fixed Livewire 3 backend layouts.
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