WAYS OF WORKING · DEMO 17 NINO CHAVEZ
The layout was
inferred, not
designed.
Aisles is a headless storefront that decides its own layout, per visitor, at request time. Same URL, same catalog — the AI infers who's shopping through 31 weighted rules and generates one of four schema-valid layouts to fit. On 2026-08-12 it was pointed at a real BigCommerce catalog for the first time.
→ or scroll to advance · ← to go back
02 WHAT AISLES IS
One engine, four shoppers it has never met
Every category page runs the same pipeline. Request-time signals (search query, referrer, UTM tag, device, time of day) and behavioral signals (category views, dwell time, cart adds) feed 31 weighted rules. Each rule contributes a log-likelihood adjustment toward one or more of four personas; the engine sums them against a gatherer-biased prior and takes a softmax to get a probability distribution. The primary persona then drives the AI layout call.
The prior leans gatherer on purpose — the exploratory layout is the safest default for a visitor the engine hasn't seen signals from yet. It self-corrects as request and behavioral signals arrive.
03 THE SCHEMA IS THE GUARDRAIL
The AI can't invent a component that isn't in the vocabulary
A model that free-writes a category page can produce anything — including something broken. Aisles constrains it instead: a Zod schema enumerates exactly four component types, and that schema is passed to the model as a structured-output contract, not a suggestion in the prompt.
SectionSchema = discriminatedUnion('component', [
editorial-header, // eyebrow + headline + body copy
hero-product, // one featured product, specs optional
product-grid, // 2/3/4 columns, landscape or square
category-header, // title, sort, filter
])
LayoutSchema.sections: 1 to 8, ordered
The model chooses which components appear, how products are ordered, and what the copy says — it cannot emit a fifth component or a malformed prop. If generation fails outright, the documented fallback is Claude Haiku → Claude Sonnet → a static Svelte layout, so a valid page always exists.
04 FOUR LAYOUTS, CONCRETELY
The same prompt reads four different rulebooks
Each persona ships its own layout principles into the prompt. The model isn't told "personalize this" — it's told exactly which components to lead with, whether quick-add appears, and how to sort.
Gatherer
"No quick-add buttons — this shopper wants to browse, not rush… copy should be warm, editorial, magazine-like."
Hunter
"Show quick-add buttons on every card… no editorial copy — no hero product, no lifestyle stories."
Researcher
"Show full specs inline on every card… no quick-add buttons — they're not ready to buy yet, they're evaluating."
Gifter
"Group or call out price tiers… copy should be warm, reassuring, and focused on the recipient's experience."
Every quote above is taken directly from the prompt-construction source — the persona definitions the model actually reads before it writes a single section.
05 THE INTEGRATION DAY
Pointing it at a real store was configuration, not code
Aisles already ran on three fictional brands — Haven, Volt, Ember — sharing no product data, only the inference engine and prompt pipeline. Adding Kibble & Co., the pet-supply storefront from the same BigCommerce sandbox as two earlier sessions in this series, meant writing a brand config entry and nothing else.
The change, committed 2026-08-12
One brand-config entry: 8 categories mapped 1:1 to the live sandbox's category tree, a harvested theme, and store copy. No route, no query, no component touched.
It worked in one pass because the catalog seam was already generic: loadCategoryProducts() reads its category map from getBrand().categories, and calls BigCommerce's Storefront GraphQL through two functions in a shared client. Add a brand, and the same function resolves a different catalog. Selecting the deployed brand is two environment variables — BRAND_ID for the server, VITE_BRAND_ID for anything the client bundle needs at build time.
06 THE PALETTE HARVEST
The colors came from the storefront, not a color picker
Kibble & Co.'s theme wasn't chosen — it was read off the live storefront's own stylesheet, the "Shelf-Native" theme, where mint is reserved for the Auto-Refill subscription signal and never used decoratively elsewhere.
Every hex value in the brand config traces to a specific line in the storefront's own CSS, not a hand-picked palette. That discipline — harvest the brand, don't invent it — is what keeps a demo built by an agent from drifting into generic-AI color choices.
07 WHAT WASN'T RIGHT THE FIRST TIME
A furniture nav sat on top of a pet-food page
The navigation component called getBrand() directly, in the client. That function reads an environment variable that is only ever set on the server — so on every page, the client silently fell back to the default brand and rendered its category list instead of the active one.
The bug, in production
Kibble & Co.'s catalog rendered correctly below the fold — but the nav bar above it showed Haven's furniture categories: Living Room, Office, Bedroom.
BRAND_ID never reaches the browser bundle, so a client-side getBrand() call always resolves to the default brand, not the one actually deployed."Categories come from the server-loaded brand — getBrand() is server-only (BRAND_ID is not exposed to the client bundle, so a client call silently falls back to the default brand)." inline comment added to the fix, src/lib/components/Nav.svelte, commit 09e074b
The fix: flow categories through the page's server load function instead of re-deriving them in the browser. The general lesson travels past this one bug — an env-selected config read in two runtimes is two sources of truth, and the client one fails silently to a default rather than erroring loudly.
08 WHAT TRANSFERS
Four habits that made a real store a same-day integration
One catalog seam
A single function reads the active brand's category map and calls the storefront API. New brand, same function, different data.
Constrain, don't hope
A typed schema passed as a generation contract makes an AI-written layout provably valid, not just usually reasonable.
Harvest the brand
Pull real colors from real CSS with a file and a line number. A palette with no citation is a guess wearing a hex code.
One runtime per config
An env var read in both server and client code needs to resolve identically in both, or the client's silent fallback becomes the bug nobody notices until it ships.
The pattern is reusable. The store is the fixture.
09 CLOSE
Four stores. One page that never stops guessing
Nothing about the URL changes, and nothing about the catalog changes. What changes is which of 31 rules fired, which persona won the softmax, and which four-component layout the model was allowed to write. Adding a real store to that engine took one config file and two environment variables — and still found a real bug the moment it ran in production.