Updates & Releases

Changelog

Follow along as we continuously build the future of Software 3.0 commerce.

Current engine release: v0.62.0 · npx create-cartwright

Every shop knows its engine version

since v0.23.0

Every Cartwright shop now ships a .cartwright/release.json marker recording the exact engine version it was scaffolded from — auto-stamped at release, so it never drifts. Paired with a published security advisories index in the engine changelog, a known fix can always be matched to the shops that need it — even though you own the code and nothing auto-updates it. Shipping in every release since v0.23.0.

v0.62.0

A critical Next.js fix for every shop, cron jobs that refuse callers without their secret, and redirects that work without Redis

next 16.4.1 closes two critical and one high upstream advisory (CW-2026-009): upgrade, or move next yourself today. A production deploy without CRON_SECRET now refuses its cron calls (CW-2026-008) — set CRON_SECRET before you deploy this release, or every scheduled job stops. Admin-managed redirects are served from the database when Upstash Redis is not configured. With urls.permalinks on, a stored permalink reaches the engine's product and category links, but is not yet served at its address, so keep it off on a live shop. No schema change: no pnpm db:push is needed. See URL options.

next 16.4.1 closes two critical and one high upstream advisory (CW-2026-009) — remote code execution in use cache functions that return typed arrays, remote code execution in Image Optimization with SVG files, and a denial of service in the App Router through Server Actions. The engine writes no use cache function and does not set images.dangerouslyAllowSVG, so the two critical ones are not known to be reachable in an unchanged shop; the App Router enables Server Actions by default, so treat the denial of service as reaching every shop. Fix it without waiting: pnpm add -E next@16.4.1 && pnpm add -D -E eslint-config-next@16.4.1.
A production deploy without CRON_SECRET refuses its cron calls (CW-2026-008, CRON-AUTH-b) — every route under /api/cron answers 503 with "CRON_SECRET not configured on this deploy" in production (a Vercel production or preview build, next start, a container), and the job does not run. next dev and tests still let the call through; with the secret set, a wrong or missing bearer is a 401 as before. All eleven cron routes go through one helper, and the admin bar and setup guide say the jobs are refused. Upgrading: generate a secret with openssl rand -hex 32, set it as CRON_SECRET in production and redeploy.
Admin-managed redirects work without Upstash Redis (RED1) — the proxy reads the redirect map from Redis when it is configured and otherwise from the Redirect table, cached for 60 seconds per server instance, so a redirect saved in /admin/redirects or imported by CSV takes effect within about a minute on the default scaffold.
A stored permalink reaches the engine’s own product and category links once urls.permalinks is on (URL5-a) — canonicals, language alternates, BreadcrumbList, the sitemap, the catalogue feed, the search API, product cards, the design packs, footer, cart, review pages and the product list’s WebMCP url. The header and mobile-menu navigation and a few other links are still on the pattern, and a permalink is not yet served at its address, so keep permalinks off on a live shop.
The review-request mail links to the shop’s own language, and the last shared hand-built storefront links go through the URL helper (URL2-d) — shared chrome parts, the Flux, Halo, Aerospace and Drive 404 pages, the blog index and its JSON-LD, wishlist and review pages. With brand.urls unset every link is the same string as before.
Dependencies moved to their newest releases inside the current majors, each public for at least three days; the next 16.4.1 cooldown exception is dropped again now that it is older than three days.

v0.61.0

A shop can run in a Node container, single-language URLs work without a prefix, and unpaid cancels return stock exactly once

The engine ships a Dockerfile and an opt-in standalone server for self-hosting. On a single-language shop, urls.localePrefix: "never" now steers routing as well as links. The engine's footer, trust badges, cookie banner, breadcrumbs and 404s link straight to the address the shop serves. Cancelling an unpaid order returns its stock exactly once, and an operator status change can no longer overwrite a cancel or payment that lands between its read and write. No schema change: no pnpm db:push is needed. See URL options.

A shop can run as a Node container on any host (HOST2-1) — the Dockerfile uses Node 22, pnpm through corepack and a non-root runtime with a health check. CARTWRIGHT_OUTPUT=standalone pnpm build writes .next/standalone/server.js; credentials, local databases, backups and mail previews stay out, and the build removes every .env* file from the standalone output so the server reads its runtime environment. Without the variable a Vercel build is unchanged; any other value stops the build. docs/self-hosting.md covers build arguments, runtime variables, Turso and SQLite on a volume. Cron jobs still need scheduling off Vercel, and file storage still uses Vercel Blob; a cron runner and S3 adapter are later work. An image of a site scaffold and the SQLite volume setup have not been run end to end.
urls.localePrefix: "never" steers routing on a single-language shop (URL1) — / and /contact answer directly, and /da/contact redirects permanently (308) to /contact. The engine’s aliases and merchant redirects land in one hop, also with trailingSlash, and canonicals, og:url, both sitemaps, llms.txt page links and JSON-LD addresses drop the prefix. /api, /admin, /oauth, /icon, /og and files are untouched. A single language alone never drops the prefix, and the build refuses "never" with more than one locale. Locale matching now reads brand.locales instead of da|en, and the cart’s WebMCP go_to_checkout no longer reads /cart as a locale. Browsers keep the 308, so switching back to "always" later can leave a redirect loop until that cache is cleared.
The engine’s footer, trust badges, cookie banner, announcement bar, breadcrumbs and both 404s use localeHref (URL2-c) — their links already have the address the shop serves with trailingSlash or localePrefix "never", without an extra redirect. This covers the footer in both profiles, trust badges wherever they render, the cookie policy and announcement audit-feed links, visible breadcrumbs and their JSON-LD steps, and recovery links in HTML and markdown. With both options unset the strings stay the same. Headers and footers a design pack brings itself, and the other hand-written links listed in the URL docs, still take the redirect.
Cancelling an order that was never paid puts its items back in stock exactly once — whichever arrives first: the admin status form, the bulk action, orders.update_status, Stripe’s cancellation notification, the nightly check or checkout’s rollback. A variant’s stock goes back to that variant. The form, bulk action and tool write “items returned to stock” on the timeline, and the tool also reports restocked: true. Stripe’s cancellation notification leaves a paid, flagged, shipped or refunded order alone. Cancelling an order after it was paid returns no stock here; a return does that.
An operator status change cannot overwrite a change that lands between its read and write (RACE-MARKPAID) — the admin status form, bulk action and orders.update_status write only if the order is still in the status they read. Otherwise nothing is written and the change is refused with “it changed while it was being updated — reload the order and try again”. The mismatch flag from Stripe’s notification or the nightly check is conditional too; refunds and disputes still always land because they record money that moved. orders.update_status also writes the timeline note the admin form writes, and an admin cancel that returned stock records restocked: true in its audit entry.
The agent rules files stop naming CSS the engine does not use (SKILL-INTERPOLATE) — the guidance skill drops its claim that details animates with interpolate-size and honours reduced motion there, and the Cursor, Windsurf and Gemini rules no longer offer interpolate-size or calc-size() as the engine’s animation tools. @starting-style stays because the mobile menu uses it. The skill also says shipping zones are stored but every order pays the flat rate.
Upgrading — no schema change, no new flag and no dependency change. The URL options stay off unless set; trailingSlash and localePrefix now steer routing, but a stored permalink is still not served at its address, so keep permalinks false on a live shop. The CRON_SECRET change announced in v0.60.0 — refusing production cron calls when the secret is unset — is not in this release; it is still planned for the next one. Set CRON_SECRET now if you have not.

v0.60.0

Two high advisories close, links come from one helper with three URL options, and the engine stops claiming what it does not do

Two high dependency advisories below next close (CW-2026-007): upgrade. A shop's product and category links now come from one helper with three URL options, all off by default, and urls.trailingSlash steers routing too. /built-with-cartwright and llms.txt stop claiming zone shipping and passkeys, more fonts are self-hosted, every cron route checks CRON_SECRET in constant time, and cancelling an unpaid order cancels its Stripe payment. No schema change: no pnpm db:push is needed. Set CRON_SECRET in production now — from the next release a production deploy without it refuses its cron calls. The docs gained URL options.

sharp 0.35.5 and source-map-js 1.2.2 (engine #614, CW-2026-007) — two high pnpm audit --prod advisories, both below next: sharp fixes the SVG decoder in Next’s optional image optimiser (GHSA-wq5f-xc86-pv6w), and source-map-js a denial of service through indexed source-map sections, reached through postcss at build time (GHSA-68fv-2mgg-jv7q). Neither is known to be reachable from a request in a shipped shop, but a shop that runs next start itself — a container or its own server — does run sharp, so upgrade regardless. Two overrides in pnpm-workspace.yaml carry the fix; run pnpm install so they land. pnpm audit --prod now reports no known advisory.
Every cron route checks its secret through one helper, in constant time (engine #621) — the eleven scheduled-job handlers under /api/cron each compared the Authorization header with !==; they now all call verifyCronRequest, which hashes both sides and compares them with timingSafeEqual, and the two machine-only admin routes that take the same bearer compare the same way. What stays open: without CRON_SECRET these routes answer anyone. A production deploy without it now says so on every admin page and in the setup guide (/admin/integrations), and from the next release it refuses those calls and its scheduled jobs stop. Set it now: generate one with openssl rand -hex 32, add it as CRON_SECRET to the production environment and redeploy; Vercel Cron then sends it on its own.
One place a shop’s public links are built (engine #609, #613, #615) — lib/urls.ts, and a urls block in brand.config.ts with three options: permalinks (keep an imported address), trailingSlash (end every path in /) and localePrefix "never" (drop the language prefix on a single-language shop). All three are off by default. The engine’s product and category links — product page, category page, product card, footer, cart, sitemap, catalogue feed, /pricing.md, the search API, IndexNow and the review pages — and the shipped design packs’ product and category tiles go through the helper, and with the options unset every link is the same string as before. permalinks does not reach those links yet, and links still written by hand follow in later releases.
urls.trailingSlash: true steers routing as well as links (engine #617) — a storefront page asked for without its slash (GET /da/product/x) answers with a permanent redirect to /da/product/x/, the way a migrated WordPress address is written. proxy.ts does it rather than Next, so /api, /admin, /oauth, /icon, /og, /.well-known and files answer with or without the slash and a POST is never redirected: Vercel Cron, Stripe’s webhooks and an OAuth callback do not follow a redirect. Merchant redirects match a source in either form, the engine’s own redirects land in one hop, and the sitemap’s entries carry the slash; a link still written by hand (the footer’s contact and info links) takes that one redirect. Off, the default, Next receives exactly the config it did before. Only trailingSlash steers routing yet, so keep permalinks and localePrefix at their defaults on a live shop.
/built-with-cartwright, llms.txt and /admin/features stop promising what the engine does not do (engine #619) — shipping zones are marked not implemented and leave the tour, because checkout still charges the flat rate (which /admin/shipping now says above the zone editor, with the fee every order pays); the passkey line is gone; and the logo generator is described by what it does rather than by a model id. A shop that turned shippingZones on still charges the flat rate at checkout, as it always did.
The AI model picker (/admin/integrations → AI) offers Claude Sonnet 5.5 and Claude Opus 5.5 instead of Sonnet 4.5 and Opus 4.5, and Haiku 4.5 stays the default (engine #610). Both get the full admin tool set and are priced in the usage meter; a shop that saved a model the list no longer offers keeps it, shown as “saved — not in the current list” until someone picks a current one and saves. An unknown model is no longer silent (engine #618): it still runs with read-only tools and an 8k context window, and the AI form now says so.
Fonts are self-hosted, so a build no longer asks Google for them (engine #616, #620) — the root layout’s Geist and Geist Mono, Brutalist’s Archivo, Space Grotesk and Space Mono, and the Space Mono of Editorial Ink and Meridian load from app/fonts/; that fetch had failed builds at random. Measured on seven homepages, each fetches the same font files and the settled page renders the same; Space Grotesk and Space Mono are now requested later, so on a cold first load their text can show its size-matched fallback for a moment. Families another pack still loads from Google stay there until their last pack moves. Editorial Ink and Meridian’s mono token reads --font-space-mono (was --font-spacemono / --font-mer-mono); a customised copy that reads the old name with its "Space Mono" fallback still gets the font.
Every WebMCP browser tool states Chrome’s three annotations (engine #624) — add_current_product_to_cart, update_cart_item_quantity and remove_cart_item are marked consequential, so the browser may ask the human first; search_products, get_cart, list_visible_products and Crema’s calculate_brew_ratio are marked read-only; navigate, filter_products and go_to_checkout, which only change the page, are marked neither. untrustedContentHint is false everywhere. No tool name, schema or moat binding changed.
Cancelling an unpaid order also cancels its Stripe payment (engine #623) — until now a customer still on the payment page could finish 3-D Secure and pay for an order you had cancelled. The admin status form, the bulk action and orders.update_status now cancel the order’s PaymentIntent, and the order timeline says what happened. A payment the customer already made is never cancelled — the note says to issue a refund from the order page; if Stripe cannot be reached the order is cancelled anyway and the note says to cancel the payment in the Stripe Dashboard. The nightly Stripe check (/api/cron/reconcile-stripe) now checks amount and currency against the order’s snapshotted rate, so a multi-currency order paid correctly no longer goes to “Needs review”, and an unpaid order it cancels gives its stock back to the right variant, never twice.
Also fixed — “Translate” on a vibe page works again (engine #618): it asked Google for gemini-1.5-flash, shut down on 2025-09-29, and sent the key still encrypted when it was saved in /admin/integrations; it now uses gemini-2.5-flash and reads the key through the same helper as every other Gemini call. Dependencies move to their newest releases inside the current majors, each public for at least three days (engine #612) — the AI SDK, the MCP SDK, next-intl, lucide-react, resend and smaller packages; no direct dependency moved a major. One dev-only advisory stays open because no fixed release exists yet (braces, reached only through eslint-config-next at lint time).

v0.59.1

Search engines and feeds see the shop as it is: the real sku, sold-out products in the sitemap, no drafts

A patch for what machines read. A product page's JSON-LD carries the product's own sku, a sold-out product stays in the sitemap, drafts leave the sitemap and the product feeds, and the Snapshot & Restore page follows the sitePack switch in /admin/features without a redeploy. No schema change and no new flag: update and deploy.

The Product JSON-LD on a product page carries the product’s own sku when one is set, instead of the shop’s internal database id (engine #608). A product without a sku, or with an empty one, keeps the id, so nothing moves for a shop that never set one; a feed, an ERP or a catalogue migrated from WooCommerce can now match the page to the product it already knows.
A sold-out product stays in the sitemap (engine #608). Until now the sitemap dropped every product with no stock, so a product that sold out vanished from it and came back on restock while its page kept answering. The sitemap, the Google Merchant feed and the agent catalogue feed now list every active product, with availability stated on the page and in the feed, and a product saved as a draft appears in none of them. The two feeds always kept sold-out products; what changes for them is that drafts are left out.
The Snapshot & Restore page (/admin/sitepacks) follows the sitePack switch in /admin/features at runtime instead of the value baked in at deploy time (engine #608): while the switch is off the page answers 404, like its menu entry, and the sitepack.export / sitepack.import tools follow the same switch, so turning it on in the admin makes them work instead of answering “SitePack is disabled”.

v0.59.0

The migration path gets its tools: redirects from a CSV with a dry run, links that keep the locale, an e-mail per paid order

Redirects can be imported from a CSV with a dry run first, the design packs' links and the product feeds keep the reader's locale instead of bouncing through a redirect, and the owner can get an e-mail for every paid order. A product page's JSON-LD names every variant, IndexNow pings sit behind a flag, the marketplace manifest carries engineFacts, a deleted product's page answers 404, and stripe is pinned exactly. The release adds columns and tables and rebuilds OrderItem in place to make productId optional — every row and id is kept — so run pnpm db:push before deploying it.

Import many redirects at once from a CSV — from,to,status — with a dry run first (engine #601): /admin/redirects gets an Import CSV panel (paste, preview per-line errors, import) and agents get the redirects.import tool (settings:write; dryRun: true previews, confirm: true applies). Valid rows are saved in one transaction under one audit entry; duplicates, chains and loops are refused with their line number. The single-create form shares the rules: a relative destination written with the /da or /en prefix is refused (the redirect keeps the reader’s locale, so it would loop), and a destination that differs from its source only by a trailing slash or a query is refused as the same page.
Links in the design packs keep the reader’s language (engine #597) — product and category cards, CTAs, service cards and newsletter forms in thirteen packs linked /product/… and /contact without a locale, so a reader on /en of a Danish shop was redirected to the Danish page. The product feeds (/feed/google.xml, /api/acp/feed) now link /<default locale>/product/… directly instead of a URL that answers 307, and a test scans every pack for the mistake written as a literal link. Not yet covered: links a pack takes from brand.config.ts and the studio section defaults.
Owner order e-mail (engine #598; ownerOrderEmail, off by default) — the shop’s admin address gets the order reference, the customer, the lines, the total and a link to the order in admin for every paid order: from the Stripe webhook, the reconcile cron, mock checkout and agent (ACP) checkout alike, once per order, always after the customer’s receipt. A failed owner mail never fails the payment; an order marked paid by hand sends none. The setup wizard’s owner e-mail field is saved now, too.
Every variant Offer in the Product JSON-LD carries the variant’s name next to its sku, price and availability (engine #606), and the page prints each variant’s name and stock state as plain text in a collapsed “Availability by variant” list under the picker, so an AI crawler that runs no JavaScript reads the same facts as the JSON-LD.
IndexNow ping on publish (engine #604; indexNow, off by default; needs INDEXNOW_KEY) — Bing, Yandex, Seznam and Naver learn about a product, page or blog post the moment it is created, updated or deleted; Google does not honour IndexNow, so the sitemap stays the main path. Every locale’s URL is sent after the response, fire-and-forget; a rename pings the old URL as well; only a production deployment pings. The key is served at /.well-known/indexnow-key.txt.
marketplace-manifest.json carries engineFacts (engine #599) — how many tools the engine registers, how many scopes an API key can carry, how many tools the admin assistant may reach and how many of those stop for a confirmation — derived from the engine’s own sources on every pnpm gen:manifest, so this site states the numbers your shop ships instead of retyping them.
The schema gains the columns the import, order-status and URL work will build on (engine #595) — a category tree (Category.parentId) and extra categories per product (ProductCategory), permalink, externalId/source on products, categories, orders and customers, product status/metaTitle/metaDescription, Order.orderNumber/termsVersion/termsAcceptedAt, an OrderItem.sku snapshot, and two empty tables, OrderStatus and NumberSequence. Nothing reads them yet. OrderItem.productId is now optional, so an order line can outlive its product; gdpr.export_user works again for a customer who has sent an inquiry (its schema lacked the v0.58.0 source and data fields).
The money path gets honest (engine #598, #602, #596) — the Stripe webhook and the reconcile cron can no longer both mark one order paid and both send its receipt; a payment that succeeds for an order no longer awaiting payment is logged for manual review instead of flipping it back to paid; and the order page’s status form goes through the order state machine like every other status change — allowed moves only, an order note, an audit entry — in English like the admin around it. Revenue on the dashboard and in analytics.summary reads one shared rule; the numbers are unchanged.
Also fixed — stripe is pinned to exactly 22.5.0, with a unit test keeping the pin and the apiVersion in lib/stripe.ts in step (engine #605); the account pages, /changelog and the blog render one <main> landmark (engine #603); a deleted product’s page answers 404 instead of still rendering; and a shop no longer points at three addresses of its own that did not answer — /manifest leaves the sitemap, /favicon.ico redirects 308 to the generated /icon, and /<locale>/om-os redirects to /<locale>/about (engine #600).

v0.58.2

A security release: next 16.3.8 closes a high SSRF advisory in Image Optimization

Next.js fixed a high server-side request forgery advisory in Image Optimization (GHSA-cjq9-62q9-8jv4). The engine's next.config.ts sets images.remotePatterns, so every shop serves /_next/image unless it removed that block. We shipped the fix outside the usual cadence. No schema change: no pnpm db:push is needed.

next 16.3.8 (engine #593, CW-2026-006) — it also fixes five medium advisories and one low one in the development server. eslint-config-next moves with it. 16.3.8 is younger than the three-day install cutoff, so its twelve exact versions replace the 16.3.6 ones in minimumReleaseAgeExclude. A shop on an older engine can fix it today: the CHANGELOG’s advisory index has the steps.
Transitive advisories closed (engine #593, CW-2026-005) — pnpm overrides force undici (under jsdom and @vercel/blob), ip-address, fast-uri and brace-expansion onto their patched lines, and dompurify moves to ^3.4.16. None is reachable from a request in a shipped shop as far as we can measure; pnpm audit --prod now reports no known advisory.
The admin order page says what the agent tool says: a refund status moves no money (engine #591) — setting refunded or partial_refund from the status form still works, but the timeline note reads “Manual registration — no money moved” and the audit entry carries manual: true and moneyMoved: false. A card refund is still made with the Issue refund button.

v0.58.1

A security release: next 16.3.6 closes a critical next/og advisory

Next.js fixed a critical remote-code-execution advisory in next/og (GHSA-vcvr-r3jv-pc5j). Every Cartwright shop since v0.17.0 renders its share images with it, in every profile. The /og route places the visitor's title as text, not as SVG markup, so exploitability is not proven. We shipped the fix outside the usual cadence anyway. No schema change: no pnpm db:push is needed.

next 16.3.6 (engine #588, CW-2026-004) — eslint-config-next moves with it. The twelve 16.3.6 packages are younger than the three-day install cutoff, so they are listed one by one in minimumReleaseAgeExclude, with the advisory next to them. A shop on an older engine can fix it today without waiting for an upgrade: the CHANGELOG’s advisory index has the steps.
An agent can no longer mark an order refunded when no money moved (engine #589) — over REST or MCP, orders.update_status refuses refunded and partial_refund unless the caller records a refund made outside the shop and sends manual: true and confirm: true (plus refundDkk, in øre, for a partial refund). The order then gets the note “Manual registration — no money moved”. A card refund is still made through Stripe, with the admin order page’s Issue refund button.
Dependencies at their newest releases inside the current majors (engine #588) — the AI SDK, @sentry/nextjs, next-intl, zod and seven smaller packages, each public for at least three days. stripe stays on 22.5.0.

v0.58.0

A custom form can say which form it was and what it collected

Every lead used to arrive as a name, an email and a message. A fence calculator that posted “42 metres, Type 1, anthracite” kept the name and the email and dropped the rest behind a 200. v0.58.0 gives a lead source (which form) and data (what the visitor configured), shown in /admin/leads. The release adds two Lead columns, so run pnpm db:push before deploying it. pnpm audit --prod reports no known advisory.

Lead source and data (engine #554) — source up to 64 characters, data a JSON object of up to 50 keys and 4 KB, nested at most 32 deep. Over a limit is a 400 that names it (source_too_long, data_too_many_keys, data_too_large, data_too_deep), never a silent cut. The no-database site profile folds both into the mail the owner gets.
Documented where an AI looks first (engine #554) — the five agent-rules files, the cartwright-guidance skill and the blank design pack now describe the built-in lead capture. An AI building a custom site had concluded it did not exist and started to rebuild it.
Dependencies at their newest releases inside the current majors (engine #586) — next 16.3.5, react 19.3.0, the AI SDK and fourteen smaller packages, each public for at least three days; stripe stays on 22.5.0. pnpm now refuses a package version younger than three days.
The design playbook says what an AI must not invent (engine #579) — DESIGN.md gains three taste rules: never invent the owner’s facts, a check for the default AI look, and the palette accent is the UI accent, not a brand colour.

v0.57.0

The line between the customer’s site and Cartwright’s own, drawn in code — and no known production advisories

A scaffold carries a few pages that belong to Cartwright, not to the site owner: our own /cartwright and /changelog answer on her domain, and her llms.txt advertises the changelog. v0.57.0 ships the engine half of taking them out: an ownership ledger that names what is the engine’s and what is the customer’s, and a test that fails on a file that is “neither”. It also adds a zone of guards that ships into every scaffold. pnpm audit --prod reported fourteen advisories on v0.56.2; it reports none now.

Fourteen advisories closed (engine #583, CW-2026-003) — fast-uri, sharp, qs, hono, mysql2 and deepmerge-ts pinned with pnpm overrides, js-yaml bumped. Shops already running an older engine can copy the overrides block into their own pnpm-workspace.yaml today and set js-yaml to ^4.3.2; a shop still on next 16.2.x moves next to 16.3.3 first (CW-2026-003 has the exact steps).
An ownership ledger for the scaffold (engine #575) — ENGINE_ONLY and SHIPS_EVERYWHERE give the manifest the two words it lacked, and every link into an engine-only page is gated on the profile. This release ships the engine half. create-cartwright 2.9.5 does not read the ledger yet.
A guard zone inside every scaffold (engine #577) — eight new guards ship in the customer’s scaffold, where the defects they catch actually show. Six are static and run in every pnpm test (dead fetches, dead scripts, identity, the engine-route ratchet, the profile marker, the guards’ own portability); two check a running site when GUARD_BASE_URL points at one — homepage links, icon, og:image and JSON-LD logo answering 2xx, and a sitemap whose every <loc> resolves and lists every served locale’s homepage.
The product page speaks the reader’s language (engine #580, #582) — twelve hardcoded strings on the product page moved into messages/{da,en}.json. The trust badges now link straight into the reader’s locale, instead of costing every click a redirect that sent a reader without a locale cookie to the default one.
Honest agent surfaces (engine #572, #578, #581) — a site scaffold’s header no longer links routes the profile removed. llms.txt stops calling the sitemap “the complete index”. The WebMCP form tools declare their required fields, following an external WebMCP audit.

v0.56.0

The plain-website door — a site scaffold is what its README says

npx create-cartwright@latest my-site --profile site now produces a plain website that boots and builds with Google's CDN unreachable, ships no cron entries into routes it does not have, installs no Postgres driver, serves its share cards and favicon, and whose contact form reaches its owner. Five defects an AI scaffolding “just a page” could not have fixed itself; four were found by review falsifiers on a real 2.9.2 scaffold. The docs gained Build a plain website and every entry surface names the door.

No crons for a scaffold without cron routes, no Postgres driver without a database (engine #563) — vercel.json became a seam owned by the db module; a site scaffold receives vercel.static.json ($schema + framework only) instead of ten cron entries whose routes the materializer deletes. The orphan pg + @types/pg left the engine root.
Font-safe boot (engine #564) — the nine Google-font design packs and the root layout’s Geist pair moved into a google-fonts module (managed-site and up). A site scaffold gets a system font stack behind the same --font-geist-* variables and eight self-contained packs; measured with Google blackholed, the first `next dev` compile answers 200 where it used to cache a 500.
The contact form reaches the owner (engine #565) — SmartContactForm asked the admin-owned /api/support/triage before /api/inquiries, unconditionally; in a site scaffold that route does not exist and every visitor saw “Could not connect to the server”. The form now reads the profile’s supportTriage capability and goes straight to the human path.
Share cards and favicon are not dead links (engine #566) — the site profile’s middleware exempted only /api from the locale rewrite, so /og?title=… (every page’s og:image) and /icon 307’d into /en/og → 404. It now routes them by the same isAssetExempt the default profile uses.
Docs: Build a plain website (new), two doors on the Introduction, the AI Agent Quick Start’s Path A, honest scoping on In the box, and measured, provenance-carrying numbers copied from the release scaffold gate — never typed.

v0.50.0–v0.50.1

WebMCP-native storefronts

The storefront page itself becomes an agent surface. With the webMcp flag on, every route registers the tools that make sense on that page via the draft document.modelContext API — the product page sells its own product, the cart edits itself, the catalogue lists and filters — so a browser agent acts through a typed API instead of guessing at the DOM. The whole surface is default-off, and with the flag off the HTML is byte-identical.

Contextual per-route tools (v0.50.0) — the product page registers add_current_product_to_cart with its variants enumerated in the schema (and REQUIRED when the product has them, so an agent cannot create the base-price line the human UI cannot); the cart page registers update_cart_item_quantity, remove_cart_item and go_to_checkout — which only OPENS checkout for the human; the catalogue registers list_visible_products (zero-network — exactly the server-narrowed list the human sees) and filter_products, whose schema is server-derived: live category slugs, the sort whitelist, real price bounds. The global registrar slims to search, get_cart and navigate.
Declarative form annotations (v0.50.0) — the site search, contact and newsletter forms carry toolname/tooldescription; WebMCP-enabled browsers surface them as agent tools, every other browser ignores the attributes. Agent-invoked submits are answered through event.respondWith with the actual outcome. Autosubmit only on search (read-only); contact and newsletter ask the human to confirm — and the checkout form is deliberately not annotated.
Design packs ship their own page tools (v0.50.0) — the new DesignPack.webMcpToolBindings seam lets a design register tools alongside its pages. First out: crema’s calculate_brew_ratio, the homepage brew calculator’s own math typed for agents — the same module the human’s widget runs, under the review-gated PURE_COMPUTE class.
The test-enforced safety moat (v0.50.0) — the browser surface is limited to products.* and cart.*: no order-placing tool exists in the browser, period. One moat test aggregates every tool surface — global, catalogue, PDP, cart and every registered design pack — and requires globally unique names; a Playwright e2e walks home → catalogue → PDP → cart → checkout and asserts checkout is tool-free.
/webmcp-check, the agent-tools showcase (v0.50.0) — the full tool inventory grouped by registering surface, built from the SAME binding constants the moat test verifies, the safety moat explained in plain language, and setup steps for ChatGPT’s built-in browser, the Chrome flag and the WebMCP Inspector — plus a live registration panel.
The whole surface ships in the light and full scaffolds behind the webMcp flag, and v0.50.1 follows with a scaffold-profile correctness patch. How it works: WebMCP.

v0.46.0–v0.49.0

The agentic day

On August 24, 2026 we pointed an independent public scanner at our own demo store and shipped four engine releases against what it found. Measured publicly by is-agentic.com: 52/100 that morning, 79/100 by evening — the day’s cleanest run landed in the 80-band. The number keeps moving with the scanner’s rubric, which is exactly why we link the live scorecard instead of framing a screenshot. Four documentable changes carried the jump: RFC 9457 problem+json on every edge of the API, a markdown-native surface for text-first agents, an MCP handshake that works for real-world clients, and a signed A2A agent card with the publish flow it never had.

The agent-ready public surface (v0.46.0) — five anonymous, rate-limited read tools over REST and MCP; OpenAPI 3.1 generated from the same tool registry the store runs, one typed operation per tool; RFC 9457 problem+json with a stable code and a resolution on every error — including unknown /api/* paths, which used to fall through to the HTML 404.
Markdown where agents read (v0.47.0) — /auth.md documents the real auth model (Bearer keys, named scopes — and says plainly that no OAuth flow is offered), /pricing.md publishes currency, VAT treatment and the live price range on webshops only, and the homepage answers as markdown via Accept negotiation, /index.md, or ?mode=agent — with YAML frontmatter and resolvable locale-canonical links throughout.
A handshake that works for real clients (v0.48.0–v0.49.0) — two measured root causes closed: the MCP origin guard gained a deployment-aware tier (public-https accepts foreign https origins — the cookie-less anonymous surface has no CSRF to protect and TLS makes DNS rebinding unreachable, while local deployments keep the strict allowlist), and the transport stopped answering 406 to clients that send Accept: application/json alone.
The signed agent card (v0.48.0) — /.well-known/agent-card.json serves an ed25519-signed A2A card buyer agents can verify offline, and scripts/publish-agent-card.ts is the publish flow the card libraries never had: ephemeral keys, self-verified, transactional rotation, no stored signing secret.
What we deliberately did NOT do — no user-agent cloaking, no fabricated rate-limit headers, no OAuth discovery documents without an OAuth server, no sameAs profiles for a fictional demo brand. A capability a store does not run is a genuine 404. It costs scanner points and buys trust; we think that trade is the whole game.
Measure your own store: npx is-agentic your-domain.com — the result is a public, shareable scorecard. How the surface works: agent readiness.

v0.43.0–v0.45.0

Identity ownership + the MCP hardening wave

Three releases that made the store harder to break from the outside and harder to confuse from the inside: brand.config.ts became the default identity authority everywhere, the seed learned to refuse destroying customer work, and the MCP/agentic surfaces closed a whole class of framework-level bypasses — while the admin went English and the engine put on its vermilion identity.

Identity ownership by default (v0.43.0) — brand.config.ts is now the default authority for store name and commerce mode, with the same invariant applied to the live canaries; a database row can no longer quietly rename a code-configured store.
Destructive-seed safety (v0.43.0) — seeding refuses to wipe a database that already contains customer work, and a safe path creates an operator on an existing production database instead.
Streamable HTTP Origin validation (v0.44.0–v0.44.1) — the DNS-rebinding protection the MCP specification requires, applied in front of every verb including the OPTIONS preflight Next.js used to answer before the guard; plus Next.js 16.3.0 and the security-patched NextAuth beta.
The OPTIONS bypass closure (v0.45.0) — framework-level preflight bypasses closed across MCP, ACP, UCP, A2A and merchant-feed surfaces, with profile-safe tests that keep the route guards preserved; locale-less route shadowing and login callbackUrl handling fixed along the way.
An English admin and the vermilion identity (v0.45.0) — the admin panel speaks English end to end, and the engine’s own brand moved from purple to vermilion — identity surfaces only, customer palettes untouched.

v0.41.0–v0.42.0

What a downstream product found

A commercial product built on Cartwright deployed to production, and reported back three rounds of findings. Almost none of them were crashes — they were silences: a value that quietly won an argument with your code, a form field that saved and changed nothing, a deploy that reported success while serving nothing. Every one is now either fixed or impossible to reintroduce without a test going red.

Identity ownership is stated, not assumed (v0.42.0) — brand.identitySovereignty: "auto" | "config" | "db" decides whether brand.config.ts or the admin database owns your store name and whether you sell. The default reproduces the old behaviour expression-for-expression, so nothing changes until you opt in; "config" is what a code-configured fork needs, after a fork toggled an unrelated flag and watched its live site rename itself in the header, the footer and llms.txt.
The admin stops storing values it will never render (v0.42.0) — under "config", the settings form, the setup wizard and the settings.update_branding tool drop the owned fields from the write and say which ones, instead of accepting input, reporting "Settings saved!" and changing nothing. Locked fields show why, and when the stored and effective values differ the form says so.
pnpm verify:deploy <url> (v0.42.0) — the check a green deploy cannot perform for you. A misconfigured project can serve your public/ folder as a static site: every route 404s, the build log is empty, nothing errors, and the dashboard says Ready. The script asks for three routes the app generates, which can only answer 200 if the framework actually ran.
The visitor no longer waits on an LLM (v0.42.0) — the contact form saved the lead only after an AI round-trip, so every enquiry paid the latency and, without an API key, logged an error per submission. Triage moved after the write, behind the default-off leadAiTriage flag; the second unauthenticated AI route the same form calls got the per-IP rate limit it was missing.
Nothing heavy at import time (v0.41.0) — the HTML sanitizer built a jsdom window when its module loaded, and it is reachable from the tool registry barrel, so a page that only wanted a lookup function could die at module load. Now lazy, and an ESLint rule keeps the whole class out.
Next.js 16.2.11 (v0.41.0, CW-2026-002) — the July security release: 9 CVEs, two of which reach every Cartwright shop because every profile ships Server Actions.

v0.37.0–v0.40.0

The site profile + the trust wave

Four engine releases in one arc: a whole site becomes a portable artifact, the storefront speaks your language end to end, the agentic surfaces go spec-true and battle-tested, and the engine learns to scaffold a real site with no database at all — while Cartwright Plus activation and a locked-down public tool surface make it ready to run for money.

The site profile (v0.40.0) — create-cartwright --profile site cuts a static, no-database site from the same engine: a cartwright-module-v1 module/profile registry, per-module file inventories, and an import-closed site file-set proven by graph tests and the release scaffold gate (light + full + site).
Cartwright Plus activation v1 (v0.40.0) — Ed25519-signed license keys + /admin/plus, env-only and schema-free; the purchase flow sits ready-to-flip.
Public-surface lockdown (v0.40.0, CW-2026-001) — /api/mcp and /api/v1/tools now honor the mcpPublic flag (404 when off) and API keys actually expire; the first entry in the engine's security-advisory index.
SitePack + content-import + the purple rebrand (v0.37.0) — snapshot a whole site to an integrity-checked .cartpack and restore it on a newer engine; scrape a URL and rebuild it as draft pages; the engine wears its own Cartwright-purple identity without touching customer palettes.
Agentic discovery, spec-true (v0.38.0) — the ACP product feed pinned to the official OpenAI schema, Google Merchant conversational attributes, and the entire cart + checkout flow routed through next-intl.
The trust wave (v0.39.0–v0.40.0) — AI-crawler taxonomy with a "block training only" policy, honest 4xx agentic errors, and the moat routes (agent card, MCP discovery, feeds, negotiate, escrow, Guardian, ACP money path, the write tools) pinned by mutation-verified test suites — 2,070 tests green.

v0.35.1–v0.36.3

First impressions + AI-agent hardening

A fresh scaffold's very first render is now a designed moment instead of leftover template copy — and the scaffold itself got harder and friendlier for the AIs building on it: three external AIs (Claude, Codex, Gemini) each built a site on the engine, surfaced real gaps, and every one was fixed at the source.

First-run Welcome Canvas (v0.36.0, flag firstRunWelcome — scaffolds enable it) — an untouched site greets you with "Your site was just born." over a palette-adaptive aurora and three paths: a copy-paste AI quick-start prompt, the guided /admin/setup wizard, and a gallery of the design catalogue. It vanishes permanently the moment the site becomes yours.
English-first scaffolds (v0.36.0–v0.36.2) — create-cartwright now scaffolds English-only (/en) sites with welcome copy instead of leftover studio hero copy, seeds setupComplete: false so the first admin login opens the setup wizard, and en-only scaffolds pass tsc out of the box (the locale cast now derives from routing.locales).
Blank Canvas + the mockup-first flow (v0.36.1) — designs/blank/ is a registered, deliberately bare design made to be rewritten: ask your AI for a completely new design — header, footer, every page — while cart, checkout, admin and the AI tools keep working underneath. New mockup.set / mockup.clear tools publish a disposable HTML homepage takeover for instant approval before the real implementation; sanitized, confirm-gated and audited.
Self-prompting design guidance (v0.36.3) — DESIGN.md, the design playbook every agent rules file points to: the three build paths, the built-ins inventory (ThreeHero + 9 scenes, svg-items, the verified GSAP recipe), taste rules and a screenshot self-verification rule. Plus Blank Canvas motion examples and stable data-cw-* hooks on every floating engine overlay.
Security hardening (v0.36.3) — three admin AI endpoints that were internet-reachable without a session check are now auth-gated behind a shared requireAdminApi(), with a regression test that walks every admin API route (25 covered) so the class can't recur. Security headers land in next.config.ts (nosniff, X-Frame-Options, Referrer-Policy, HSTS, Permissions-Policy; CSP report-only), plus a timing-safe per-IP + per-email login brute-force throttle.
Dark mode, unified (v0.35.1) — Tailwind dark: utilities and the theme-token overrides now follow one switch, so a fresh scaffold no longer half-flips when the visitor's OS is dark. The admin is decoupled and always the light Polaris skin; storefront dark returns later as a per-design opt-in.

v0.35.0

The Light release

Cartwright is now told — and built — around one sentence: the build engine AIs reach for. A real site with design, database and backend, live in minutes. npx create-cartwright defaults to a lean light profile, heavy modules become installable plugins, the Mixer matures into a full composition system, and the AI onboarding path is measured: a cold agent goes from scaffold to a designed, verified homepage in 99 seconds. The engine is now formally MIT-licensed.

Light by default — create-cartwright scaffolds the light profile: website-mode, 8 curated designs, lean modules (−18 packages); --profile full keeps everything, byte-identical to before.
Plugin system (cartwright-plugin-v1) — optional engine modules as manifest-declared plugins with install state and an admin API. First five: phone-widget, wishlist, blog, reviews and three-scenes (the entire Live Canvas 3D system).
Compositions (cartwright-composition-v1) — a whole look (skin + palette + voice + chrome + scene + layout) as a downloadable, uploadable artifact with dry-run import and an atomic composition.apply tool.
Chrome as parts — 14 design headers/footers + 4 neutral parts are selectable and mixable per shop; the chrome registry derives from the design registry.
Magic speed — parallel section generation (~5× faster), streamed progressive build UI, and an instant 0-LLM preset path.
AI onboarding, measured — "Your first 10 minutes" in every scaffold: a verified terminal-only path from boot to a designed homepage in 99 seconds, plus motion & animation guidance with a verified GSAP recipe.

v0.34.0

The metamorphosis release

The release where the design catalogue becomes a unified design language: two flagship designs shipped in one day, a library of installable SVG ornaments, and a signature motif giving every premium pack its own recognizable chrome. The public Mixer lets anyone compose a Skin × Voice live, and a new manifest + capture infrastructure means every gallery derives from one source — so growing the catalogue is now ~free.

Stillwater — a calm-enterprise flagship design with generative SVG landscapes rendered in four times of day, palette-adaptive end to end.
21 SVG items — premium, palette-adaptive ornament components (9 of them CSS-animated), installable via the registry.
Signature-motif chrome — all 14 premium packs now carry their own recognizable motif through header, footer and section chrome.
The public Mixer at /mixer — compose any Skin × Voice pairing and preview it live in the real storefront layout.
marketplace-manifest v2 — scenes, svg-items, elements, looks and motifs in one manifest; every gallery derives from this single source.
pnpm capture:gallery — an automated preview-capture pipeline, plus registryStats install metrics for the component registry.

v0.34.0

FABLE — the metamorphosis flagship

A launch-day flagship celebrating Claude Fable 5: a 25th design where an instanced flock of 3D butterflies flutters behind an airy ivory story page, paired with its own launch-announcement Voice and a curated Look. Premium, website-mode, and palette-adaptive end to end — the whole flock and every section re-tone to your brand.

A new butterflies 3D Live-Canvas scene — an instanced, palette-reactive flock with procedural wings, flap/glide motion, pointer scatter, and a reading-clearing around the headline. Inherits the WebGL2 / reduced-motion / save-data gating like every scene.
The FABLE design (premium, website-mode) — ivory + morpho violet-blue, a Fraunces serif display hero over the flock, a scroll-cinema metamorphosis timeline (animation-timeline: view(), caterpillar → chrysalis → imago), a stat band, a safeguards story and a CTA.
The fable Voice — the Fable 5 launch story as a vertical preset: metamorphic, luminous, precise, with its own genome copy, palette, and the butterflies scene.
The Metamorphosis Look — the curated Skin × Voice pairing: the FABLE design carrying its own launch-announcement Voice, in your brand colours.
Gallery hover-video — the /designs gallery now plays FABLE’s recorded scroll-through on hover (webm + mp4 + poster), like the other animated heroes.

v0.33.0

The Page Mixer + the Apex flagship

Content and design are orthogonal in Cartwright, so this release lets a shop mix a vertical Voice (kindergarten, carpenter, café…) with any palette-adaptive design and compose the page from swappable Parts — plus a much deeper premium-design layer: a design now owns every page, four breakthrough Pro elements, per-design webshop overrides, and Apex, a flagship super-pro storefront that composes all of it on one palette-adaptive page. Additive and default-off end to end — an existing shop is byte-identical until it opts in.

Voice layer (genome) — the homepage hero + section copy now resolves through the Resolvable Genome, so a Voice can re-tone the whole page; the value-prop and feature cards are voiceable too. Gated by genomeResolve; every anchor equals the current brand copy, so flag-off is byte-identical.
Vertical / Voice presets — a verticals/ registry where a Voice carries the full vibe (identity + genome copy + palette + 3D scene), applied idempotently from /admin/verticals. The Page Mixer (mixer-preview) renders any Skin × Voice composition in the real storefront layout, noindex + double-gated.
Four recognizable premium designs — aerospace (cinematic deep-tech), halo (minimal product luxury), flux (vibrant gradient SaaS), drive (full-bleed automotive) — plus jungle, a friendly palette-adaptive site. A design now owns every page via the shell model (siteChrome + per-page contact/info/404 templates).
Four Pro elements (honor-system cartwrightPlus) — a build-your-own configurator (live price, :has(:checked), no JS), a scroll-cinema story (animation-timeline: view()), a 3D product showroom, and a before/after compare slider. Per-design webshop overrides (productCard + PDP) land too.
Apex — the flagship super-pro design: one palette-adaptive webshop homepage composing every breakthrough above, where every section and Pro element re-skins to your brand palette via applyPaletteAsTheme.

v0.32.0

Design Slaraffenland — the design marketplace

The premium-design marketplace foundation: a growing catalogue of code-built design packs, reusable three.js, design.md import and export/share, an agent-buildable design path, and a companion /designs marketplace on cartwright.app.

Five new code-built premium designs — engineered (dark-luxe agency, three.js GLSL aurora hero) as the flagship, plus editorial-ink, brutalist, nocturne (+3D) and meridian. Each a real designs/<slug>/ pack, English-first, locked theme, three.js opt-in.
A reusable three.js aurora scene + DesignHero wrapper so any pack gets a palette-driven 3D hero (inherits WebGL2 / reduced-motion / save-data gating).
design.md export / download + share — the missing half of design import: a serializer + an export API + a download button, so a shop can share a design. Plus a prompt → design pipeline and a cartwright-premium-design skill teaching agents to hand-build bespoke packs.
The cartwright.app /designs marketplace — a Figma-Community-style gallery with search + filter, per-design detail pages, build prompts, and a prompt library.

v0.31.0

Motion & effects — pages that feel alive

A flag-gated layer of modern CSS scroll-driven animations (compositor-thread, no JS jank), an animated palette-adaptive aurora gradient + glassmorphism, and an optional per-section motion vocabulary the Visual Builder can assign. Default-off and canary-safe end to end.

Motion foundation — a new motionEffects flag + a motionPreset block in brand.config.ts (subtle | bold | off). Off ⇒ no rule matches ⇒ byte-identical render.
Per-section effect vocabulary — a governed enum (fade-up | fade | zoom-in | slide-left | slide-right | parallax | none) the Magic Builder planner can assign per section.
An animated Aurora hero — aurora-site wraps its hero in the animated gradient with the three.js hero behind it as an opt-in; the gradient is the guaranteed fallback.
Every animation is compositor-only, inside prefers-reduced-motion: no-preference, with scroll-driven effects behind an @supports guard — unsupported browsers stay static.

v0.30.0

Agent-optimized design — read it, build with it, cite it

The design system (Aurora + the Magic Builder + the section catalogue) is now optimised for AI agents end to end: they can read it (registry + schemas), build with it (the builder tools), and cite it (Schema.org JSON-LD).

Section JSON-LD — pages built from a section layout now emit Schema.org structured data server-side (faq → FAQPage, howItWorks → HowTo, galleryGrid → ImageGallery, testimonials → Review, pricingTable → ItemList) so AI search engines can cite them. Honest by construction — no fabricated ratings or prices.
An installable component registry — /api/registry serves real, MIT-licensed, npx shadcn add-able TSX for a curated subset of section atoms, alongside the always-on prop JSON-Schema for every section.
A section-vocabulary skill teaching external AI agents the whitelisted section types + the data-not-code doctrine before they generate, so their output is valid and on-brand.
Discovery surfaces (llms.txt, /.well-known/mcp.json) advertise the new capabilities — flag-gated so nothing links a disabled endpoint.

v0.29.0

Magic Builder + the Aurora flagship design

Two big additions: a prompt-driven page builder, and a new flagship default design system — built on one shared set of section atoms, so the two are the same components.

Magic Builder (magicBuilder, default-off, admin-only) — describe a page in plain language and it builds itself, section by section, live in the Visual Builder preview. The prompt can only emit a plan of whitelisted section keys, each filled by AI with Zod-validated props — the model never picks a tag, colour or font. Output lives as governed, audited, one-click-revertible data, never code on disk.
A ~20-section curated catalogue (hero, value-props, feature-grid, how-it-works, testimonials, pricing-table, FAQ, gallery, CTA…) — client-safe, accessible, and shared by both the builder and the new default design.
Aurora — the new flagship default design for both website (aurora-site) and webshop (aurora-shop), composed from the catalogue and palette-adaptive: one design renders every brand in its own colours (applyPaletteAsTheme), retiring the old "every webshop looks the same" default.
A public, shadcn-compatible component registry (/api/registry) exposing each section's prop JSON-Schema so external AI agents and IDEs can target Cartwright sections.

v0.26.0–v0.28.0

Admin, restructured & re-skinned + bulletproof first-run

A run of admin + developer-experience releases: the admin sidebar becomes a calm, grouped information architecture; the whole /admin backend is re-skinned to a clean light Shopify-Polaris look; and first-run database setup is made deterministic so onboarding can never get stuck. Admin-only and storefront-neutral — the three canaries render identically.

Grouped admin IA (v0.26.0) — the flat ~40-item sidebar becomes two pinned items above seven ordered, collapsible groups (Sales · Content · Intelligence · Marketing · Connections · Appearance · System). Nav is a single typed source of truth; a group auto-hides when all its items are flag-gated off. No schema change, default-equivalent.
A modern admin skin (v0.28.0) — a scoped [data-admin-skin] token override re-skins every admin surface to a light Polaris palette (white cards, fine borders, dense tables, a sticky top bar with ⌘K global search) without renaming a single storefront token and with zero storefront impact. ~58 admin pages migrated to shared primitives.
Bulletproof first-run DB setup (v0.27.0–v0.27.1) — a new pnpm db:setup routes around an intermittent Prisma 7.8 schema-engine flake by applying the schema via the libSQL client, so a brand-new shop reaches admin login with no manual recovery. Postgres-aware, fails loudly on real errors, and re-running never wipes data.
pnpm admin:reset — recover a lost or drifted admin password (resets only the password, keeps all data, and rewrites .admin-credentials so it always matches the DB).

v0.25.0

Agentic commerce, completed: buy-in-ChatGPT, agent identity-linking & in-browser tools

Three agentic-web surfaces move from scaffold to wired: ACP delegated-payment completion, a full UCP OAuth identity-linking server, and WebMCP in-browser tools. All default-off and canary-safe — an existing shop is byte-identical until it opts in — and the ACP payment path ships code-ready but inert until external Stripe + ChatGPT access lands.

ACP delegated-payment completion (acp + env ACP_PAYMENT_COMPLETION, default-off) — the last piece of the ACP checkout lifecycle: /complete now charges via a Stripe Shared Payment Token (off-session PaymentIntent) and builds the order from the ACP session line items, with idempotency replay and refund-on-failure. Wired and unit-tested behind the env gate; it responds 501 (inert) until Stripe SPT access and ChatGPT merchant onboarding are connected, so it can never accidentally move money.
UCP identity-linking — an OAuth 2.0 server (ucpIdentityLinking, default-off) — implements dev.ucp.common.identity_linking: a full Authorization Code + PKCE server so an agentic platform can act on a shopper’s behalf across merchants. Ships RFC 8414/9728 metadata, RFC 7591 dynamic registration, /oauth/{authorize,token,revoke}, a consent screen, and a sample protected resource (/api/ucp/orders). Only token hashes are stored; run pnpm db:push and set AUTH_URL before enabling.
Security-hardened after an adversarial review — refresh-token reuse detection (a reused rotated token revokes the whole family), client-bound revocation, a canonical issuer (never the Host header — so issuer-spoofing and metadata cache-poisoning are out), and a least-privilege registration default with an unverified-app consent warning.
WebMCP in-browser tools (webMcp, default-off) — the storefront registers search_products, get_cart, add_to_cart and a same-origin navigate via document.modelContext, so an in-browser AI agent acts reliably instead of scraping the DOM. Experimental (Chrome 149 origin-trial, W3C draft) — kept off the canary mosaic.

v0.24.0–v0.24.2

Onboarding clarity, secure first-run & dark-mode polish

A DX + polish run across three patch releases: a fresh shop is sign-in-ready regardless of approach (CLI, IDE agent, or manual clone), the migration baseline is clean again, and the admin — plus the storefront — renders correctly in dark mode.

Onboarding & first-login clarity (v0.24.0) — create-cartwright now bootstraps the DB (db push + seed) so the admin exists and .admin-credentials is written before you open the app, and every surface points at password-first login (magic-link appears once Resend is set).
Reliable first-run (v0.24.1) — the auto db push retries once on the transient Prisma 7.8 engine error and surfaces real failures; the migration baseline was regenerated from ~50-migration drift into a single clean from-empty baseline (db push stays the canonical path, so the live canaries are unaffected).
Admin dark-mode contrast (v0.24.2) — a 33-page admin audit replaced hardcoded Tailwind colors with theme-flipping sol-* tokens so form/tool surfaces are readable in both themes; the /admin/hoptify nav item is now gated on its flag (no more 404).
Storefront dark-mode scoping (v0.24.2) — dark mode is admin-only now: the root ThemeProvider no longer follows OS dark (defaultTheme="light", enableSystem=false), so brand storefronts always render their designed palette instead of a half-dark mess. The admin toggle still works; the Teloz showcase stays dark by design.

v0.23.0

Visual Builder + Vercel v0

A governed, no-code page builder — and Vercel v0 as a native generation engine. Design a page in a three-panel UI, or let v0 turn a prompt into UI; either way the output lands as audited data you own, never code dumped to disk. Both ship default-off and canary-safe, so a shop that does not opt in is byte-identical to before.

Visual Builder (visualBuilderEnabled) — a three-panel editor at /admin/visual-builder: a section list (add / reorder / hide), a live-preview iframe, and an inspector. Output is stored as a validated section tree in Page.layoutJson (hero / featureGrid / ctaFooter / richText) — audited data, never TSX written to disk. A null layout renders from your existing body/vibeHtml, so the storefront is unchanged until you build a page.
Governed writes — every change goes through the pages.set_layout tool with a plan-first confirmation token, an audit-log entry, and one-click revert. The AI "generate section" action fills a section's own schema-validated props, so the model can never emit arbitrary markup. A shared PageSections component renders both the preview and production, so what you see is what ships.
Vercel v0 generator (v0Generator) — a second AI engine in the Vibe Sandbox alongside Anthropic, and wired directly into the Visual Builder as a governed vibe section. v0 turns text into UI; Cartwright normalizes the result to HTML, sanitizes it, and persists it as vibeHtml — code becomes governed data, nothing is written to disk. Bring your own v0 key (encrypted in the DB or via V0_API_KEY); a daily-usage guard keeps you under v0's limits, and a GDPR processor entry is added automatically.
Default-off and canary-safe — both flags ship off. Run pnpm db:push to add the additive Page.layoutJson + four IntegrationSettings columns before enabling.

v0.22.0

AI-native commerce: semantic search, generative UI & agent surfaces

Your catalog becomes semantically searchable, the storefront assistant composes its own product UI, and the agent-commerce surfaces (ACP, UCP) move closer to complete. All additive — semantic search falls back to lexical when embeddings are not primed, so there is no regression.

Hybrid semantic search — vector cosine-similarity + lexical boost over a product-embedding index, wired into both the search API and the products.search tool, with a soft fallback to lexical search when embeddings are not ready. Embeddings come from Gemini (text-embedding-004) with a local Ollama fallback; backfill with pnpm embeddings:backfill.
pgvector / Postgres acceleration (opt-in) — for large catalogs, push the nearest-neighbour search into Postgres with a pgvector HNSW index — same ranking, identical results, logarithmic instead of linear. Enabled with DATABASE_DRIVER=postgres and pnpm pgvector:setup; the default Turso/SQLite path is untouched. Runs on Supabase Postgres.
Model-selectable generative UI — the storefront chat now lets the model choose how to present products — grid, spotlight, or comparison — via a whitelisted tool (it picks a layout + product slugs; the server fetches the data, never arbitrary markup). XSS-safe and read-only.
Agent-commerce surfaces — a UCP native_commerce capability marks catalog products as native-buyable by agents (gated so the shop never advertises what it can't honor), and the ACP checkout session gains a structured, inert /complete scaffold behind a default-off env gate (it can never accidentally move money). Plus token-level cost-metering on the AI chat routes, so spend is observable.

v0.21.0

Google Sheets, Drive & Docs + Stripe Subscriptions

The Google Workspace modules on top of the v0.20.0 connector, plus recurring billing. Everything is additive and default-off, so a shop that does not opt in is byte-identical to before.

Google Sheets sync (sheetsSync) — two-way sync between a Google Sheet and your catalog via the Sheets API v4: pull upserts products by SKU and never deletes; push clears the range first so a shrunk catalog leaves no stale rows; each run reports added/updated/skipped. CRON_SECRET-gated cron + /admin/sheets. Default-off.
Google Drive media + backup (googleDrive) — import images from a Drive folder into the media library (reusing the MediaAsset + Blob pipeline with sha256 de-dupe) and push the logical DB/media backup to Drive as an off-Vercel copy. CRON_SECRET-gated cron + /admin/drive. Default-off.
Google Docs import (docsImport) — turn a Doc into a draft blog post or info page. The converter emits Cartwright engine markdown (never HTML); content is stored as text and rendered through the safe renderContentBlocks path, so a shared Doc with <script>/javascript: cannot become stored XSS. Default-off.
Stripe Subscriptions (subscriptions) — recurring billing on Stripe Billing with an admin list (cancel-at-period-end) and a self-service customer portal (start/pause/resume/cancel, scoped to the signed-in user). Webhook handling is additive and flag-gated; one-off checkout is unchanged when off. Run pnpm db:push before enabling. Default-off.

v0.20.0

Google Workspace connector, Sign-In, FX auto-refresh & storefront i18n

The Google integration foundation plus two gap-closers. The connector is fail-soft infrastructure (inert without credentials); the rest ship flag-off, so an existing shop is byte-identical until it opts in.

Google Workspace OAuth2 connector (no flag) — one shared server-side connector (lib/google) behind Sheets/Drive/Docs: bring your own Google Cloud client (encrypted in IntegrationSettings or env), incremental per-module scopes, CSRF/PKCE-protected flow, skew-aware single-flight token refresh, local-authoritative disconnect. Fail-soft: no credentials means every Google surface is inert.
Google Sign-In (googleAuth) — a "Continue with Google" button on customer login via NextAuth, mirroring the GitHub provider (uses the OAuth-ready Account table, no new model). Admin is never granted via OAuth. Default-off; needs GOOGLE_CLIENT_ID/SECRET.
FX auto-refresh (fxAutoUpdate) — refresh exchange rates daily from the ECB no-key feed into a DB override, read as dbRate ?? staticAnchor. Display, checkout and the receipt resolve the same rate (no drift). Flag off → static brand.config anchors, exactly as before.
Storefront translation rendering — saved Product/Category translations now render on the storefront (PDP/PLP/category: name, description, metadata, alt text, breadcrumbs, JSON-LD), closing the v0.15 gap where translations were saved but not displayed.

v0.19.0

Secure-by-default accounts & onboarding

The "finished package" customer surfaces and an onboarding/credential revamp. The headline: no more hardcoded admin password. Most items are additive; a few columns need pnpm db:push before enabling.

Secure-by-default admin credentials — the seed generates a strong random admin password (no hardcoded default) and forces a change on first login at /admin/konto; the password is also written to a gitignored file so a new owner can always find it.
Password reset & account settings — /account/forgot-password → email → /account/reset-password (HMAC-SHA256 single-use token peppered with AUTH_SECRET, 1h TTL, no account-existence leak), plus /account/settings to edit profile and set/change a password.
Contact-form image attachments (contactAttachments, default-off) — image-only, size-capped, magic-byte-validated uploads to Vercel Blob, shown in the admin inbox.
Default legal pages + self-service GDPR export — privacy/terms/cookie pages render from templated defaults when no CMS page exists, and customers can download their full data as JSON from /account.

v0.18.0

Marketing automations, Prisma 7 & a security-docs pass

A platform-hardening release. Resend — already your transactional transport — becomes an opt-in lifecycle email engine; the ORM jumps to Prisma 7's Rust-free client; and the security surface gets documented end to end. All additive and default-off: a shop that does not opt in is byte-identical to before.

Marketing automations (marketingAutomations) — emit welcome, abandoned-cart, and post-purchase events to Resend Automations, which runs the drip sequences you wire in its dashboard. Cartwright emits events only (it stays the source of truth, not a marketing-automation platform), and emission is consent-gated to confirmed newsletter subscribers. The abandoned-cart cron emits the event instead of a single mail — no double-send. Default-off; inert without a Resend key.
Prisma 7 — the data layer moves to Prisma 7's Rust-free prisma-client generator and driver adapters (libSQL/Turso for production, a local file adapter for dev). No schema changes, so existing databases need no migration; new scaffolds db push from zero as before.
Security & architecture docs — four new guides ship in the box: API-key security (HMAC-SHA256 keys peppered with AUTH_SECRET so a DB leak alone yields no usable key), the scope model + per-tool map, the MCP endpoint architecture (stateless transport, curated introspection — never your DB schema), and an advanced Supabase/Postgres path (RLS on, Data API off).
Dependency refresh — Stripe apiVersion 2026-05-27.dahlia (SDK 22.2.0), native Tailwind v4.3 scrollbar utilities on the storefront panels, and removal of a stale npm lockfile (the project is pnpm-only).

v0.16.0

Agent-editable shop: structure, theme & catalog by AI + JSON

Where in-place editing (v0.14.0) lets you click and rewrite copy, this makes the shop's structure editable by agents and machine config — homepage section order, theme tokens, and the catalog itself. All default-off / additive: a shop that does not opt in is byte-identical to before.

Section layout (sectionLayout / layoutJson) — an AI agent reorders or hides studio homepage sections via the design.set_layout MCP tool, with required sections (hero, CTA) protected. Default-off, runtime-toggleable, and revertible via the audit log. Flag off → the hardcoded section order is byte-identical.
Extended themeJson — fonts and corner-radius are now an injection-guarded superset of the 6-colour palette, so an agent can set --font-sans / --radius-sol-* from the same theme override the colour palette already used. A current palette renders identically.
products.json machine-seed — seed an entire catalog from a validated JSON file (pnpm seed) instead of editing TypeScript. Prices are in øre (machine-precise), with per-row validation errors that fail fast.
Setup safety — a fail-fast env preflight surfaces a missing AUTH_SECRET / database URL on boot with a one-line fix, instead of a confusing downstream error.

v0.15.0

Sell in every currency, in every language

Day-one internationalisation, both halves. Checkout now charges in the customer's currency — not just shows the price in it — and the storefront translates Pages, Services and blog posts, not only products. All additive: a single-currency, single-language shop is byte-identical to before.

True multi-currency (multiCurrency) — the Stripe charge is created in the customer's selected presentment currency with the converted amount, and the order snapshots currency + FX rate so receipts, refunds and exports reproduce exactly what they paid. currencySwitcher stays the display-only gate; flip multiCurrency to upgrade from "show in EUR" to "charge in EUR".
One conversion path (lib/money.ts) shared by display, charge and receipt — the shown price is always the charged price. 2-decimal-safe with a guard rather than a silent mis-charge.
Multi-language breadth — supported locales live in brand.config.ts (add German in one line: ["da","en","de"]); routing + hreflang light up automatically. The /admin/translations editor and render path extend from products/categories to Pages, Services and blog posts.
Migration: the Order table gains currency + fxRate — run pnpm db:push before enabling multiCurrency. Multi-language needs no migration.

v0.14.0

In-place AI editing: edit your live storefront by clicking it

Cartwright's owned take on click-to-edit, on infrastructure you own. Logged in as admin, toggle edit mode on your live storefront, click a copy element, type a plain-language note ("make this headline shorter"), and an AI proposes new copy shown as a before→after diff before it applies. One default-off, admin-only, base-locale-only flag — the storefront is byte-identical for everyone else.

In-place editing (annotateEdit) — an admin-only overlay highlights editable copy on the live storefront; click one → write a note → AI proposes → before/after diff → confirm. Wired across footer copy, hero headline/sub-line, product name/description, page title/body, and category name. Off → no data attributes and no overlay render at all.
The model never picks a tool. During propose it runs with no tools (a pure text transformer); a deterministic allowlist (lib/annotate/targets.ts) maps each target → write-tool and excludes anchored legal copy. Apply reuses the plan-first confirmation-token spine (args-hash bound, owner-scoped, one-time-use), so tampered copy is rejected — and every edit lands in the audit log under an annotation: actor.
New settings.update_copy tool for the hero headline/tagline (single-column read-modify-write) — single-field edits never blank sibling branding columns, and the existing settings.update_branding the admin chat uses is untouched.
Base-locale only in v1 (the write tools have no locale param yet); per-block page editing and localized editing are deliberately out of scope. Default-off — flip annotateEdit in /admin/features to dogfood it.

v0.13.0

Order management: an HPOS-grade back office

Turns /admin/ordrer from a flat list into a real back office: a scalable Orders workspace, admin-initiated returns/RMA, printable pick lists, and rule-based next-best-action. Everything ships behind four default-off, ecommerce-gated flags — an upgrade behaves exactly as before until you flip one, and website-mode shops never mount any of it.

Order workspace (orderWorkspace) — status tabs over a 12-status lifecycle governed by a pure state machine (the 9 existing statuses kept verbatim; new admin-only processing/delivered/completed), server-side search + cursor pagination, bulk status actions with per-order skip reporting, exception flags (delayed / low-stock / needs-attention), an order notes + status timeline, tracking entry, resend-confirmation + send-shipping-notification, and a manual refund button. Off → the legacy order table is unchanged.
Fulfillment & pick lists (fulfillmentPdf) — a print-friendly packing-slip / pick-list route (browser → "Save as PDF", no PDF dependency) plus a one-click create-fulfillment that reuses supplier routing.
Returns / RMA (returns) — admin-initiated returns: create → approve/reject → receive + restock → refund. Receiving restocks idempotently (a return restocks exactly once, even on a retry); refund reuses Stripe with the webhook as the single status-writer. Refund returns money, a return returns money AND stock — deliberately decoupled.
AI next-best-action (orderAi) — a deterministic rule engine surfaces the next sensible action per order (ship now, follow up on delivery, review a flagged payment, process a return) as ranked, deep-linking chips. Pure and free; an optional model-backed layer sits on top when a provider key is present.
Manual + dashboard refunds finalize reliably — charge.refunded now resolves the order via the payment intent when the charge has no orderId metadata (Stripe doesn't copy it). All four flags default-off; run pnpm db:push once to add the additive OrderNote / Return / ReturnItem tables + nullable billing-address columns (lossless).

v0.11.0

GEO: make AI recommend your shop

A GEO / AI-discoverability release. The engine now describes itself to AI assistants as a recommendable product and ships broader agent-rules coverage — so when someone asks an AI to "build me a shop," Cartwright can be in the answer. Additive and gated by the default-on, deletable cartwrightBadge flag.

SoftwareApplication JSON-LD on /built-with-cartwright — describes Cartwright the product (free, npx create-cartwright, source repo, $0) so AI crawlers can surface it as a recommendable tool. Scoped to that one page so a shop's storefront still reads as its own business; gated by cartwrightBadge.
"Built with Cartwright" block in every shop's llms.txt — gives an AI the recommendation payload directly: the install command, the source repo, and cartwright.app.
Agent rules for Gemini + Windsurf — GEMINI.md and .windsurfrules join the existing Claude / Cursor / Copilot rules so any IDE agent that opens a scaffolded project recognises it as Cartwright.
cartwrightBadge flag — default-on and deletable, like "Made with Framer". It gates all three signals (footer badge, SoftwareApplication schema, llms.txt block) via getFeatureView, so toggling it off in /admin/features removes them everywhere.
Docs + README revamped for GitHub and AI-training discoverability. This changelog and the homepage now read the engine version straight from the engine CHANGELOG, so the version never goes stale.

v0.10.0

The big one: 12 feature tracks

The largest single release — twelve subsystems land in one tag, every one opt-in and default-off so an upgrade behaves exactly as before until you flip a flag. The authoritative flag list is lib/feature-flags/manifest.ts, and the customer-facing /built-with-cartwright tour now renders itself from that manifest, so it can never go stale at release time again.

Resolvable Genome (genomeResolve) — copy fields render override ?? resolved-cache ?? brand anchor, harmonised against your identity anchors. Render never calls an LLM; resolution is triggered in /admin/genome. Respawn a shop's whole voice from a sentence.
SEO/GEO Lab (seoAutopilot, ⭐ Pro, beta) — measures AI-citation share-of-voice (GEO) at /admin/seo-performance. GSC measurement + automated genome experiments (apply → measure → keep or revert) are scaffolded and still in development.
Hoptify (hoptify + logoGenerator) — a tongue-in-cheek Shopify pendant: a familiar storefront design plus a parody "import from Shopify" onboarding that genuinely brings your palette (design-import) and products (Firecrawl scraper) across when a key is set, fail-soft to a demo otherwise. Includes a Gemini raster logo generator.
Firecrawl scraper + Design import (designImport) — scrape product data from a URL, or pull a colour palette from any site into a live theme in ~2 minutes. /admin/design-import.
Blog (blog) — /blog + RSS + BlogPosting JSON-LD + sitemap, edited from /admin/blog. Content marketing without a second CMS.
Shipping & fulfillment (shippingZones) — zone/weight rates with delivery times + dropship-supplier routing, or a flat rate. /admin/shipping.
Tax / VAT (stripeTax) — managed multi-country VAT via Stripe Tax (EU OSS, VAT-ID) with invoicing, or the built-in single-rate. Correct at checkout either way.
WooCommerce parity — wishlist, abandoned-cart recovery, admin redirects, product CSV import/export, and a translation-management UI. The migration toolkit.
GDPR / DSAR — data-subject export + soft-erasure, retention crons, and a processor register at /admin/processors. Compliance built in, not bolted on.
Backup + indexing controls — automated DB backup cron, plus per-shop noindex + AI-crawler allow/block toggles wired into robots.txt and meta robots.
All flags default-off and ship in the engine v0.10.0 tag — npx create-cartwright pulls them once the template ref bumps. See the engine CHANGELOG for per-feature setup + required keys (FIRECRAWL_API_KEY, BLOB_READ_WRITE_TOKEN).

v0.7.0

Design System: pick a design, import from any AI tool

Decouple visual design from industry seed-data. Previously a Cartwright shop was locked to one homepage component per industry-template slug. Now design is a first-class orthogonal axis — any industry combines with any design — and you can drop a design.md from Gemini Stitch, Claude Design, or v0 into /admin/designs to install it. Ships with 4 website designs and 4 webshop designs out-of-the-box, including 3 brand-new webshop variants (minimal, editorial, bold).

New designs/ registry — first-class DesignPack abstraction with BrandingSettings.designSlug for explicit selection. Pick any design with any industry. Backwards-compat: shops upgraded from v0.6.0 with designSlug=NULL render identically via inferDesignFromIndustry().
cartwright-design-v1 spec — YAML frontmatter + Markdown body. Defines palette (6 core + extraTokens), fonts, animations, and a sections array (hero / value-props / feature-grid / how-it-works / stack-grid / cta-footer / opaque). Round-trips cleanly through parser + serializer.
Gemini Stitch adapter — drop a .md export from stitch.google into /admin/designs, hit "Import" with the Stitch adapter. Normalizes Stitch field names (colors.primary → palette.accent, sections[].kind="features" → "feature-grid") to cartwright-design-v1. ~30-second end-to-end.
Claude Design / v0 / Loveable adapter — drop a raw .tsx file with the claude-design adapter. Best-effort hex-frequency palette extraction + regex headline/tagline scrape + sensible placeholder sections. Bridge to round-trip-edit, not a full parser.
/admin/designs hub — card grid of installed designs with active-indicator and Auto-mode badge. Drag-drop upload with adapter selector (auto / cartwright / stitch / claude-design). Click any card to activate; revalidates / immediately.
3 new webshop design variants — webshop-minimal (Apple-like full-bleed hero + oversized typography), webshop-editorial (magazine split-screen with story-card products, ⭐ Pro), webshop-bold (neo-brutalism with terracotta + electric-yellow paper, ⭐ Pro).
4 existing implicit designs moved into the registry — saas-dark (Antigravity dark/indigo SaaS), studio (cartwright.app warm-tech, ⭐ Pro), corporate-baseline (generic cinematic), webshop-classic (the pre-v0.7.0 default). All git-mv with history preserved.
Power-user CLI — tsx scripts/design-import.ts <file> [--from <adapter>] [--force]. Offline equivalent to /admin/designs upload; same parser + codegen + registry-update pipeline.
Theme integration — lib/theme.ts:designToInlineCss() emits design pack tokens at page render. BrandingSettings.themeJson layers on top (last-write-wins) so per-shop palette fine-tuning still works on top of any design.
Setup wizard — new Design dropdown in brand-step. Filters by mode (ecommerceEnabled hides webshop designs and vice versa). Default "Auto (xxx)" preview shows what the inference would resolve to.
New docs section — /docs/designs covers overview, picking, design.md spec, Stitch import, Claude Design import, writing your own.

v0.3.0

Voice Shop & Local AI v2

Customers talk directly to your shop via Gemini Live, and you can run the whole admin AI on a free local Ollama model. Every AI call is provider/model/modality-stamped in the audit log so /admin/audit can filter "voice only" or "Gemma only" in one click.

Voice Shop (Gemini Live) — floating mic-FAB on storefront, browser↔Google WebSocket with ephemeral tokens, server-side tool dispatch with same audit-log + scope-guards as text chat. Default off; opt-in per shop via brand.features.voiceShop. Default allowed tools cover products.search/get + cart.add/get_summary + discounts.try_apply (orders.create requires opt-in).
BotID-protected token-mint in production + per-IP rate-limit (3 burst, 1 per 20 min) + pre-committed setup with lockAdditionalFields so the browser can never expand the tool list mid-session.
Voice cap admin: maxMinutesPerSession + maxMinutesPerDay configurable in /admin/integrations, with VoiceUsageSection injected into the new AiStatusPill so you see today's minutes burned at a glance.
Local AI v2 — chatModelResolved(intent) returns {handle, provider, model, capabilities}. Backwards-compatible with the legacy chatModel(). New MODEL_CAPABILITIES matrix tiers Claude 4.5/4.6/4.7, Gemma 4 (e2b/e4b/e4b-mlx/26b/31b), Gemma 3, Llama 3.x, Qwen — read-only / low-risk-writes / all.
Ollama in admin: /admin/integrations "Hent modeller" button auto-detects Apple Silicon (offers -mlx variants), SSE-streaming pull with live progress bar, delete-with-confirm + total-disk-usage display, install-card with brew/curl/winget tabs when Ollama is offline.
AiStatusPill on every /admin/* page — fixed bottom-right badge with 30s health-check polling. Four states: 🔒 Local AI, ☁️ Cloud AI, ⚠️ Auto degraded (recently fell back), ❌ AI offline.
Setup-wizard 3-way AI step — Cloud (Anthropic) / Lokal (Ollama with live probe) / Spring over. Local branch auto-discovers and auto-selects first model when Ollama responds.
Audit-log stamps — every AI-driven tool call gets provider/model/modality/sessionMinutes stamps via AsyncLocalStorage withAuditContext. Old rows backfilled to provider="anthropic", modality="text". /admin/audit can filter by modality (text vs voice) or provider (anthropic vs local vs google).
Vibe generators always-cloud — theme + product-SEO + category-SEO use chatModelResolved("vibe") which forces Anthropic even when aiProvider="local", because structured JSON output from local models is hit-or-miss.

Headless Merchant: A2A Foundation

The full Agent-to-Agent architecture lands. Cartwright shops can now serve buyer agents end-to-end — signed Agent Card discovery, deterministic negotiation, Verify-then-Pay escrow with cryptographic Proof-of-Task-Execution, and human-in-the-loop oversight via /admin/agentic. All gated behind brand.features.a2a so your storefront stays clean unless you opt in.

Three new A2A endpoints: GET /api/agent-card (signed ed25519 JSON), POST /api/negotiate (deterministic engine), POST /api/escrow/verify (state machine + PoTE).
Anchor-and-Resume negotiation engine — pure TS, monotonicity-guaranteed, 800+ property tests per CI run. No LLM imports allowed in the kernel.
Guardian middleware as the Adjudication branch of Separation-of-Power: every agentic call validated against shop legislation before money moves. Fails closed to deny-all by default.
P2K scanner: blocks any commit that imports an LLM and a money/policy primitive in the same module. Architectural enforcement, not a lint suggestion.
Five industry templates: --template website-corporate | coffee | sunglasses | agent-marketplace | generic. Each sets brand.mode + brand.features defaults to match the archetype.
/admin/agentic dashboard — live A-JWT verification feed, escrow positions, disputed-escrow review queue, Agent Card snapshot. Read-only first; write-side incoming.
EscrowTransaction + PoTEProof + AgentCard + AgenticJWT — four new Prisma models with state-machine-enforced transitions.
CLI: --ref stable|next channel aliases (stable resolves to latest tag, next opts into bleeding-edge main).
Vibe Coding sandbox at /admin/vibe-sandbox — push raw HTML from Cursor or v0 via /api/admin/vibe/push, auto-translate via Gemini.
Phone.inc telephony scaffolded at /admin/telefon — IVR config, voicemail transcription, call routing. Preview while Phone.inc API spec stabilises.
Setup wizard at /admin/setup — DB-first secret storage; no env file editing required.
/integrations top-nav page launched — 15 core service integrations, plus the 10 planned Plus-tier MCP integrations (Klaviyo, Mailchimp, QuickBooks, Notion, Airtable, HubSpot, Slack, Zapier, ShipStation, Algolia) with real brand logos, category eyebrows, and deep-anchor /integrations#plus.
/onboarding redesigned as Plus-tier preview — type a URL, watch a five-agent migration demo (DOM analyzer, brand extractor, product mapper, SEO generator, ACP deployer), join the waitlist.

Software 3.0: Vibe Templates & AI Localization

Our biggest update ever. We introduce Vibe Templates, allowing you to build and inject infinitely scalable layouts directly via Cursor or Vercel v0.

Vibe API: Inject custom Tailwind HTML live from Cursor.
Global Auto-Translation: Built-in Google Gemini Flash automatically translates Vibe designs globally while preserving code.
Themes & AI Design dashboard in the admin panel.
New Setup Wizard with email and Vercel domain flow.
Setup wizard at /admin/setup walks brand → theme → API keys → first category → completion. Every key round-trip-validated before save.
Stripe Elements wired with appearance block driven by brand.config.ts — Payment Element + webhooks + mock fallback for local-dev work without real Stripe traffic.
Vercel domain flow integrated into the setup wizard — pick a domain, Cartwright prints the DNS records, polls until verified.

Local AI & Agentic Commerce

Make the platform independent with local AI via Ollama, and make your product catalog machine-readable with the Agentic Commerce Protocol (ACP).

Gemma 4 integration via Ollama for free on-device inference.
MCP (Model Context Protocol) Server endpoints enabled out-of-the-box.
llms.txt generation for AI search engines (Perplexity, OpenAI).
Phone.inc Cloud telephony integration directly in the browser.
Anthropic SDK + Gemini SDK both wired via the Vercel AI SDK — same chatModel() accessor, swap providers with one field in IntegrationSettings.
i18nexus cloud strings for storefront copy translation — no manual locale-JSON management.
DB-first secret storage via lib/secret-encryption.ts (AES-256-GCM): paste a key in /admin/integrations, no .env edit, no redeploy.

The Golden Stack & Multi-Tenant Architecture

A complete rewrite of the core engine to run B2B SaaS and Webshops on the exact same platform without compromises.

Next.js 16 and React 19 foundation for extreme speed.
i18nexus cloud integration for standard texts.
Dark Mode SaaS support out-of-the-box.
Stripe Checkout B2B subscriptions added.
Turso (libSQL) as the default database — point-in-time restore, edge-replicated, managed SQLite that survives a Vercel function cold start in under 50ms.
Sentry error tracking + performance traces wired with build-time source-map upload via SENTRY_AUTH_TOKEN.
Upstash Redis for lib/rate-limit.ts — per-IP and per-API-key throttling without standing up your own Redis.
Custom domain on Vercel: /admin/setup walkthrough prints the exact A/CNAME records, then verifies Resend SPF/DKIM/DMARC on the same domain so transactional email is deliverable from go-live day.

Initial Release: Cartwright Engine

The first version of our vision for a self-custody e-commerce platform.

Prisma DB schema and basic CRUD for products and pages.
Admin panel with Stripe Webhooks.
Vercel Edge Network optimizations.
NextAuth magic-link sign-in via Resend transactional templates — no third-party identity vendor lock-in from day one.
Resend wired for order confirmation + magic-link emails, with a .mail-previews/ dev fallback so local development never burns email credits.
Vercel Blob image uploads with signed URLs and per-product image variants — no S3 bucket setup, no CloudFront config.
One-click Vercel deploy with the storefront + admin compiled as a single Next.js app — no separate dashboard hosting.