The anatomy of great companies Partner with us
We Case Study

How Ariana CMS Built a Bridge Between AI-Generated Themes and a Content Management System

Ariana CMS lets an AI model design an entire website and keeps the CMS out of the design for good. Here is how its theme contract failed, what changed, and what anyone building an interface for AI agents can learn from it.

Ariana CMS theme contract connecting an AI-generated website design to the content management system

Ariana lets an AI model design an entire website, then keeps the CMS out of the design business for good. The hard part turned out not to be the compiler. It was the theme contract: instructions that a machine could follow without ever seeing the system run.

Ariana CMS is a content management system built on one unusual rule: the admin panel never touches the design. A site's entire look arrives as a static HTML package, usually produced by an AI model such as ChatGPT or Claude. Ariana compiles that package once and from then on manages only content.

That arrangement depends on a single document, the Theme Contract, which tells a model exactly how to turn a design into a package Ariana can run. In the nine weeks after version 1.0, Ariana shipped more than 250 releases, most of them small patches. A surprising share of the serious bugs in that period came down to a gap between what the code did and what the Theme Contract said it did. When the reader of your documentation is a machine, every stale sentence ships as a broken website.

What follows is how the contract failed, what the team changed, and what anyone building an interface for AI agents can take from it.

Key takeaways

  • If an AI model reads your documentation, that documentation behaves like code. The model will do whatever it is told and will assume that anything left unmentioned does not exist.

  • Lists of what a system offers should be generated from the running system. Ariana builds its token reference by rendering a throwaway post and recording what the renderer produced.

  • When the docs and the runtime disagree, fix the runtime if the promise was reasonable. Packages already built on that promise start working instead of becoming "wrong."

  • Security belongs in the grammar. A small, closed template language is easier to defend than a list of things authors are asked not to do.

  • Let the platform decide whether a block exists and let the theme decide where it goes. Settle that before the page renders, not by the order tags happen to appear.

  • Test the rendered page. A test that checks the text of a document or the string a compiler produced can pass while the site is broken.

  • Write for a stranger. The people who built a system are the worst judges of whether its instructions are complete.

The bet: a CMS that never edits the design

Most content management systems let the admin panel reach into the design. Theme customizers, page builders and block editors all exist so that a non-developer can change how a site looks. WordPress is the familiar example, and the flexibility comes at a price that anyone who has maintained a WordPress site knows well: design decisions scattered across plugins, settings screens and database rows, with no single place that says what the site is supposed to look like.

Ariana's founder, Ali Dezhami, started from the opposite assumption. If an AI model can produce a complete, polished static website in an afternoon, the design does not need an editor. It needs a safe way into the system and a system that keeps its hands off it afterward.

So in Ariana, a theme is a zip file. It holds a theme.json manifest, one HTML file per page, templates for the blog and archives, partials for shared fragments such as the header, and the assets. On upload, Ariana validates the archive, compiles each HTML file once into a Laravel Blade view, registers routes, creates a database row for each page (title, address and SEO fields only) and generates the sitemap, robots.txt and llms.txt. From then on the panel manages posts, media, users, menus and settings. To change a headline on the home page, you change the theme and upload it again. Posts, media and SEO edits survive the swap.

The project's engineering log records the principle that came out of this, confirmed by the founder on August 19, 2026 as permanent: Ariana is an operating system, and the theme is the program. The theme says what the site is. Ariana guarantees how it runs: storage, mail, security, validation, caching, languages, SEO, media.

That principle settled real arguments. When a contact-form builder was proposed for the panel, the founder turned it down, writing that Ariana should not drift toward becoming WordPress with a form builder. Instead, a theme declares its own forms in its manifest, with whatever fields the design needs, and Ariana validates, stores, protects and emails the submissions. The panel holds data that flows. It never holds structure.

It is a clean idea. It also puts enormous weight on one interface. Between a model that has never seen Ariana and a server that will execute whatever the model writes, there is exactly one document.

Why the Theme Contract is the hard part

The stranger problem. An AI model building a theme is a contractor who will never visit the building site. It cannot run your code. It cannot stop halfway through to ask a question. It reads every sentence literally, uses whatever it is told exists, and quietly skips whatever it is not told about. Where the instructions have a gap, it fills the gap with defaults from other systems it has seen.

A human developer forgives bad documentation. They notice the gap, open the source, search a forum, or ask someone. A model does none of that. It builds exactly what the document describes, with complete confidence, and the result is uploaded to a customer's live site.

The first version of the Theme Contract looked like any careful specification: the zip layout, the manifest schema, a closed template grammar and a short list of golden rules. The grammar is deliberately small. Double braces print an escaped value, triple braces print trusted HTML for a short list of approved fields, and there are loops, conditionals and partials:

{{#each posts}}
  <article class="card">
    {{#if this.thumbnail}}<img src="{{ this.thumbnail }}" alt="{{ this.title }}">{{/if}}
    <a href="{{ this.url }}">{{ this.title }}</a>
  </article>
{{else}}
  <p>No articles yet.</p>
{{/each}}

Rule zero of the contract has never changed: the rendered theme must be pixel-identical to the source design. Convert only what has to become dynamic, and copy everything else verbatim. Do not rename classes, do not "modernize" the CSS, do not swap the grid system. If the source had three cards in a grid, keep one as the template for the loop and let the grid do the rest.

The compiler did what it was supposed to. What followed, over two months of field reports, was a run of failures in which the code worked and the document did not.

Four failures that reshaped the Theme Contract

1. The feature the document hid

Around release 1.62.0, an outside AI model working from Ariana's generated Theme Contract audited the document against itself. It found three places where the document quietly contradicted its own claims.

The worst was a feature that existed but could not be seen. The token catalogue hid the "related posts" data as internal plumbing, while the same document described a settings switch for showing related posts. The model concluded, reasonably, that the feature did not exist, and asked whether the sidebar card it had just built would ever be filled.

The second problem ran the other way: sites with the shop switched off were being told about shop-only tokens, so a model could build markup for a buy button that could never appear.

The third was a numbering gap. Feature sections were numbered per installation, and when a feature was off its section disappeared, leaving the numbers to jump. The model read the jump as a section lost in transit and asked where section 7 had gone. A person would have skipped past the gap. The model treated it as missing data, which is the right instinct for a reader that cannot check.

2. The sentence that punished obedience

From version 1.66.0, the contract recommended {{#unless this.in_stock}} for out-of-stock badges on product cards. The compiler did not support unless. Every model that followed the instructions exactly printed the raw tag as literal text on every product card on the site. The better the model was at following instructions, the more reliably it broke the store.

3. The first real customer theme

The first theme a real customer built was produced by an AI agent working only from the generated Theme Contract for version 1.68.8. It passed both stages of validation, installed cleanly and returned a server error on the home page.

The customer traced the fault and sent back a diagnosis. It listed five problems, and none of them was the model's.

  • The contract said {{else}} could be used inside any block. Inside a loop, the compiler turned it into code that was not valid PHP, and six pages failed on every request.

  • A partial called from inside a loop could not see the current item, so every card on every archive page rendered blank, with no error to explain why.

  • One sentence claimed that site-wide values could not be reached from inside a loop. That was simply false. The agent, trying to fix the server error, believed the sentence, removed a working token from its loop and shipped a second broken version.

That last point is the one worth dwelling on. A wrong sentence does not fail once. It also misleads whoever, or whatever, is debugging the failure.

4. The at sign

Blade, the template engine underneath Ariana, treats @ as the start of a directive. To keep theme markup from being read as code, the compiler doubled every @ it found, on the assumption that Blade would turn @@ back into @ everywhere. Blade only does that at word boundaries. On a bilingual manufacturer's site, every email address in the theme came out as info@@ in the rendered page, in every mailto link, in both languages. The theme's author had done everything right. The fix was to escape @ only where Blade actually looks for one. Because compiled themes are frozen at whatever version installed them, Ariana now also recompiles every installed theme after each update, so a compiler fix reaches a site without anyone uploading anything.

The audit

These reports led to a full audit of both of Ariana's contracts (the theme contract and a second one for plug-in feature packages) against the code, in version 1.68.7. It found forty discrepancies. Ten of them broke a package outright. The other thirty were text that described behavior slightly differently from how it actually worked, which is harmless for a person and not harmless for a model.

What Ariana did especially well

1. It stopped maintaining the reference by hand

The most consequential change was also the simplest. The list of tokens a theme can use is no longer written by anyone. The class that produces it explains why in a comment that reads like a design principle:

"A hand-written token reference is wrong the moment someone adds a token and forgets the documentation … So the reference is not maintained."

Instead, Ariana runs its own rendering pipeline against a throwaway post, a throwaway archive page and a throwaway static page, then walks the data the renderer produced. Whatever the engine puts in front of a template today is what gets documented today. A token added in a future release shows up in the Theme Contract on the day it ships, without anyone remembering to add it.

The contract is also generated per site, from the admin panel. It carries that installation's language and text direction, the features it has switched on, and choices the operator made in settings, such as where a particular button should sit. A disabled feature leaves no mention at all. A dedicated test suite builds the contract in every combination of features and checks four properties each time: nothing from a disabled feature leaks in, every promised switch comes with the data it controls, section numbering has no gaps, and changing a setting changes the very next copy of the contract.

2. It wrote for a reader who had never heard of Ariana

On September 7, the founder put a blunt question to the Theme Contract. The people who built Ariana know everything about it. A customer's ChatGPT or Claude knows nothing. Is the contract enough on its own?

It was not. The contract listed every rule and every token and took the rest for granted: what Ariana is, that nothing is ever built with Node, which language and text direction the site publishes in. Ariana was built first for Persian-language publishers, so right-to-left layout is not an edge case. Yet a customer pasting the contract into a fresh chat could get back a theme in English, laid out left to right, with a JavaScript build step the server would never run.

The fix was a primer at the top of every generated Theme Contract: what Ariana is, the constraints it runs under, the facts of this particular site, a glossary for every family of tokens, and a table mapping each template to the web address it answers. The panel also gained a Help button beside the contract that spells out the working loop for the site owner: copy the contract, paste it into a new chat, describe the site, collect every file, zip them, upload, and if the upload is refused, paste the refusal back into the chat. Ariana's validation messages were rewritten with that last step in mind. They are addressed as much to the model as to the person.

3. It treated every sentence as a promise the code must keep

When the audit found the contract and the runtime disagreeing, the team had two ways to resolve each case: change the text or change the code. The rule it adopted is the idea that travels furthest beyond Ariana. If the promise was reasonable and the fix was safe, change the code. Change the text only when the code was right.

The reasoning is about who had already built on the promise. Themes had been written using {{#unless}}, so the compiler learned {{#unless}}. Themes had been written with {{else}} inside loops, so a loop's {{else}} became the empty-list branch, the way it works in Handlebars, the template language models have seen most. Partials called inside a loop now receive the current item. Every theme built in good faith against the old wording started working instead of becoming a customer's problem.

The same rule is now part of the definition of done. Every change to Ariana has to answer one question: can a theme see or do anything differently now? If the answer is yes, the Theme Contract changes in the same commit. If the answer is no, the commit message says so.

4. It put the security boundary in the grammar

Everything the compiler emits is eventually executed as PHP, and Ariana has its own AI assistant that can generate themes from model output. So every theme package is treated as untrusted, regardless of who uploaded it.

The defense is the small grammar. The compiler accepts a closed set of forms, and anything that does not match is printed as plain, escaped text, never passed to the evaluator. The design has a side benefit that matters for AI authors: a correctly formed tag with an unknown name renders as nothing, while a malformed tag prints as literal text, so a mistake is visible on the page instead of hidden.

A closed grammar is still only as good as its edges, and the edges needed work. A later review, in version 1.73.2, found that raw PHP opening tags in a theme passed straight through Blade and ran on the server, and that on hosts configured to accept PHP's short opening tag, even an ordinary XML stylesheet declaration could take a page down. It also found that template names were being used as file paths, so a name like ../../x wrote a file wherever it pointed. Both were closed at the compiler, which also protects themes that were already installed, since each release recompiles them. The validator additionally refuses such a package at upload with the file named, so the author gets a clear answer.

The same thinking runs through theme-declared forms. The declaration in theme.json is the security boundary: the server accepts exactly the declared fields and nothing else, uploaded files are checked by their contents and not only by their extension, and a theme whose form markup does not print the anti-forgery and anti-spam tokens is refused at upload. A theme with an unprotected form should fail on the day it is installed, not on the day the spam arrives.

Since version 1.68.9, Ariana also parses the PHP it is about to write before writing it. As the project's notes put it, valid template grammar is not yet valid PHP, and the only good time to learn the difference is before the first visitor does.

5. It separated "whether" from "where"

Some blocks appear on an article without the theme asking for them: a summary box, an audio player for articles that have one, and a button inviting readers to add the site as a preferred source in Google. Ariana decides that such a block exists. The theme should decide where it goes.

The arrangement: a theme that prints the block's token places it exactly there, and a theme that prints nothing gets the block attached automatically. That keeps themes written before the feature existed working, and gives newer themes full control.

The first version chose between the two at render time: whichever printed first claimed the block. On a bilingual site in version 1.68.2, a theme that followed the Theme Contract to the letter placed its table of contents above its sidebar button, exactly as the contract's own example showed. The automatic attachment rode on the table of contents and printed first. The result was the button stuck inside the table of contents and an empty bordered card where the theme had asked for it. No ordering the author chose could fix it, because the contract itself dictated the order.

A render-time check could not solve this, since by the time the theme's own token is reached, the automatic copy has already been written to the page. So Ariana now reads the compiled theme before rendering, notes which of these tokens it prints, and skips the automatic attachment for those. Print order is no longer part of the contract.

A similar split governs layout counts. How many cards a loop holds is a design decision, so the theme declares it in its manifest. Where the operator also has a setting, such as posts per archive page, the theme's number seeds that setting once, on first install, and the operator keeps the final say after that.

6. It was strict about what breaks and lenient about everything else

Ariana's validator draws a firm line. A package that would produce invalid code, execute server code or expose an unprotected form is refused. A package with a mistake that would fail silently, such as a partial that does not exist or a misspelled SEO key, installs with a warning that names the problem. Warnings never block, because a theme that installed yesterday has to install today.

That compatibility promise extends across versions. The Theme Contract is now at version 1.4, and a theme built against version 1.0 still installs and renders unchanged. Later versions wrote down behavior that already existed or added features a theme can opt into: multiple languages, listings, a shop, declared forms.

Risks and limits

Risk

Why it matters

Current state

The contract keeps growing

The static contract is now over 1,600 lines, and a model's attention is finite

The per-site version drops every disabled feature, but a site with everything switched on still gets a long document

Every design change is a rebuild

Fixing a typo in a hero headline means regenerating and uploading the theme

Ariana's assistant can edit an installed theme's source and return a new package, but the loop is still slower than editing text in a panel

Promises accumulate

Because broken promises are fixed in code, the grammar can grow and never shrink

Deliberate: compatibility with existing themes is worth more than a smaller language

Models differ

The same contract produces different results in different models

The checklist, validator and refusal messages narrow the gap without closing it

Customers found many of the bugs

Several failures above were reported from live sites

A standing rule to update the contract with every change, plus periodic full audits of the contract against the code

The ceiling is what can be declared

Anything the contract cannot express is a site that cannot be built

The stated direction is always to widen what a theme can declare, never to add structure to the panel

A playbook for anyone building an interface for AI agents

The lessons here are not specific to content management. They apply to API documentation, tool definitions for AI agents, plugin specifications and any other interface a model will build against.

  1. Name the reader. If a model will read the document, assume it has never heard of you, cannot run your code and will not ask questions.

  2. Generate the lists, hand-write the explanations. Tokens, fields, endpoints and options should come from the running system. People should write the parts that need judgment: why, when, and what to avoid.

  3. Tailor the document to the instance. Leave out what this installation cannot do. For a model, an unavailable feature that is mentioned is an invitation to use it.

  4. Keep the language closed. Accept a small grammar and render anything else as inert, visible text.

  5. Validate what you will execute, in the form you will execute it. Checking the source is not the same as checking the compiled output.

  6. Separate errors from warnings by consequence. Refuse what breaks the site or its security, and warn about what merely fails quietly.

  7. Settle conflicts before rendering. Do not let the order in which things happen to appear decide who wins.

  8. When the docs and the code disagree, keep the promise people built on. Change the code if the promise was reasonable.

  9. Make the contract part of the definition of done. Every change answers whether it alters what an outside author can see or do.

  10. Write refusals that can be pasted back into the chat. The error message is the next prompt.

Final lessons

Ariana's bet was that AI would make design cheap and that a CMS could stop pretending to be a design tool. The bet has held up. What the team did not expect was where the difficulty would move. It moved out of the compiler and into the sentences that describe it.

Traditional documentation is written for readers who can compensate for it. They skim, they infer, they check. A model compensates for nothing. That makes it a harsh critic, and also a useful one. Every place the Ariana contract failed was a place where the system and its description had quietly drifted apart. People had been working around those gaps for weeks without noticing. The model hit every one of them.

The practical result is a change in how the team works. The Theme Contract is no longer documentation for the product. For anyone building a site on Ariana, it is the product, and it is held to the same standard as the code.

Frequently Asked Questions

What is a theme contract?

It is the specification an AI model or a designer follows to turn a website design into a package a system can install. In Ariana it covers the file layout, the manifest, the template language, what the system injects on its own and a checklist to run before delivery.

Why not let administrators edit the design in the panel?

Because design edits made through settings screens tend to scatter a site's look across many places. Keeping the design in one package means there is always a single, complete description of how the site should look, and changing it is a deliberate act.

Does Ariana depend on a particular AI model?

No. The site owner copies the generated contract into whichever assistant they use. The contract is written for a reader that knows nothing about Ariana, which is also why it works with human designers.

How does Ariana protect a site from a malicious or broken theme?

Themes are installed into a staging area, validated and compiled before anything live is touched. The template language is closed, server code is refused, compiled output is parsed before it is written, and theme-declared forms accept only their declared fields.

What happens to content when the theme changes?

Posts, media, users and SEO settings are stored separately from the theme. Uploading a new theme replaces the design and leaves the content in place.

Do these lessons apply outside content management?

Yes. Anyone publishing tools, APIs or specifications for AI agents faces the same problem: a reader that trusts every sentence and cannot check any of them.