Abstract
AI can already write correct code, yet software delivery hasn’t gotten faster — because AI is still working within architectures designed for human collaboration. This manifesto proposes an architectural pattern built around the cognitive limits of AI: an application is decomposed into pages; pages are made of mutually independent components; each component combines a frontend module with a dedicated backend function and is small enough to fit entirely into a model’s context window; pages are assembled at runtime by a config-driven rendering framework. With this architecture, AI can handle development, deployment, and end-to-end verification independently, while humans move from producing solutions to reviewing them. The manifesto also includes a draft open standard for the architecture (Section 12), which any team can implement on its own infrastructure.
Contents
The last mile
Over the past two years, AI’s ability to generate code has approached — and in some areas surpassed — that of human engineers at a remarkable pace. And yet an awkward fact remains: most teams aren’t shipping software any faster.
A product manager uses AI to build a convincing prototype in ten minutes. Then what? The prototype gets screenshotted, pasted into a requirements doc, scheduled into the next two-week sprint, and re-implemented by engineers inside a million-line codebase. What AI finished in ten minutes takes the organization a month to put in front of users.
This is the “last mile” problem: between a product prototype and a production application stands the entire traditional delivery machine.
This manifesto makes three claims:
- The problem isn’t the model — it’s the architecture. AI is strong, but it’s working on architecture designed for another era.
- We can design an architecture around the cognitive limits of AI, allowing AI to close the full loop of development, deployment, and verification on its own.
- That architecture can be defined as an open standard, decoupled from any particular company’s infrastructure.
The pain: AI is strong, delivery isn't faster
For product managers: getting AI to produce a prototype is no longer the problem, but turning that prototype into something shippable still requires the engineering team. The faster the prototype comes together, the more glaring the wait to get it prioritized becomes.
For engineers: getting AI to write code is no longer the problem, but integration, testing, deployment, and verification still require human intervention. Coding may account for only 20% of a delivery cycle. AI has compressed that 20% to almost nothing, while the remaining 80% of the delivery process has barely changed.
For organizations: AI productivity tools have made product and engineering teams faster in isolation, but the shape of the organization hasn’t changed — the same role boundaries, the same handoff chains, the same iteration cadence. Overall throughput has barely budged.
Three symptoms, one root cause.
The root cause: architecture optimized for a different cost function
AI is strong — but it’s still coding on architecture built by and for humans, and that keeps most of its potential locked up.
The dominant architecture today is microservices: a logical application is a constellation of services, each with its own repository, developed, deployed, and operated independently. It has two properties that are fundamentally at odds with AI.
First, the repositories are still too big for AI. Microservices carved the monolith into “micro” services, but each service repository can still contain tens or hundreds of thousands of lines of code. That imposes a heavy cognitive load: before it can start working, the AI has to search, guess, and stitch together the context relevant to the task from a mass of code. Searches miss things and guesses go wrong, so AI-generated changes still fail often enough to require human engineers as a backstop. The industry’s mainstream remedies — better code retrieval, bigger context windows, repo-level indexing — are all patches over the same underlying problem: the code doesn’t fit.
Second, traditional architecture is optimized for humans, not for AI. It prizes reuse and readability: systems are layered, with lower layers more generic; each layer defines its own data models (DO, DTO, BO, …), and moving data between layers generates piles of glue code that has nothing to do with the core logic. In its own era this was exactly right — the dominant cost of a system was engineering labor, and reuse and convention reduced duplicate work and hedged against turnover. The tradeoff was repositories that could grow without bound, while understanding any single feature required traversing several layers and repositories.
In one sentence: architecture is the shadow of the cost function.
When labor was expensive, we invented reuse, layering, and DRY, accepting higher comprehension costs in exchange for less rewriting. In the AI era the cost function has flipped: the marginal cost of generating code is heading toward zero, and the scarce resource is the context a single task requires. When the cost function changes, the architecture’s optimization target should change with it:
| Traditional architecture | AI-friendly architecture | |
|---|---|---|
| Scarce resource | Engineering labor | The model’s effective context |
| Optimization target | Less duplicate work (reuse, layering) | Lower cognitive load per task (boundaries, self-containment) |
| Duplicated code | A sin (DRY) | An acceptable cost |
| Cross-module coupling | A manageable price | The number-one debt |
| Unit of delivery | Application / service | Component |
This claim does not have to remain theoretical; it can be measured. We built an open benchmark, Contextile-Bench, to do exactly that. It gives the same coding agent the same set of product tasks on two implementations — a conventionally layered application and a set of self-contained Contextile components — and scores both through black-box tests as each codebase scales from S to L.
The results draw a clean dividing line. On small repositories — anything under ten thousand lines — the two architectures are neck and neck: however the code is organized, the agent’s first-pass success stays above 93%. Past the hundred-thousand-line mark, the paths diverge: first-pass success on the traditional architecture slides steadily as the application grows (100% → 96.9% → 91.7%), while Contextile finishes the largest-scale test with a perfect 24 out of 24.
The answer is not teaching AI to swim in a million-line repo. It’s breaking the ocean into fish tanks.
The proposal: AI-friendly Architecture
3.1The core model: application → page → component
The design premise: an application is a set of pages; each page is a set of components; “application” no longer exists as a deployment concept. The component is the smallest unit of development, deployment, and operations, and components are independent of one another — think of each component as a self-contained application at a much finer granularity.
Each component has two parts:
- A frontend module that renders the component and is published directly to a CDN;
- A dedicated backend function that serves dynamic data to this component alone, deployed on FaaS.
The frontend component and backend function are paired one-to-one: each function serves exactly one component.
3.2The page renderer
A page is no longer something you build — it’s assembled at runtime from configuration.
Users visit a fixed page URL carrying a pageId. The renderer asks the page-config service for that page’s configuration, which declares which components make up the page, where each component’s frontend code lives, and the identifier of its backend function. The framework loads all component code in parallel, requests initial data from all component functions in parallel, and renders each component the moment its data arrives — no component blocks another.
This yields a key property: you ship components, not applications. Adding or changing a component means publishing that component’s frontend artifact and function, then publishing a new version of the page config; rolling back means pointing the config back at the previous version. Nothing “application-level” ever gets built or deployed, so application-level release risk simply doesn’t exist.
3.3Seven design principles
- The component is the smallest unit of delivery. Development, deployment, operations, and rollback all happen per component. There are no application-level releases.
- One component = one frontend module + one dedicated function. Frontend and backend are understood and modified in the same context. There is no separate integration step because the two sides are designed and delivered together.
- Full context. A component must be small enough to fit entirely into a model’s context window, with ample room left for reasoning. This is a hard constraint, and the first principle of the whole architecture.
- Context integrity over reuse. Duplication between components is allowed; coupling between components is forbidden. Duplication is a cost, coupling is a debt — and AI has driven the cost of duplication to the floor while doing nothing about coupling.
- Pages are configuration. Page structure is data, not code. Changing a page = publishing a new config version; rolling back = moving a version pointer.
- Infrastructure must be machine-operable. Deployment, invocation, testing, and log queries must all be APIs an AI can call — not console buttons reserved for humans.
- The environment is the only source of truth for verification. AI’s self-testing loop must run against real rendering and real calls — screenshots, DOM, console, network — not the model’s opinion of its own code.
Why decompose into components
Componentization is the heart of this architecture. It breaks a large application into small, independent components, allowing AI to work at the component level without having to understand a giant repository all at once.
Cognitive load is eliminated. A component is small — hundreds to a few thousand lines — so AI can load its entire frontend and backend into context. No grepping through tens of thousands of lines to find what the task touches, no guessing whether a function has callers elsewhere, no five layers of abstraction to internalize: what it sees is all there is. That is exactly how the agent works in Contextile-Bench’s main experiment: it completes product tasks end to end on this architecture, with no human backstop, and passes the full black-box acceptance suite on the first attempt in 94 of 96 runs (98%).
Blast radius is contained. The component boundary is the blast radius. A change can break at most one component, and a component failure has an explicit fallback policy at the page-config level (hide, skeleton, fallback message). The benchmark makes the boundary visible: across 80 Contextile-Bench runs at S, M, and L scales, the traditional architecture produced 8 net regressions in code outside the intended change, while Contextile produced zero. This turns “let AI modify production code” from a gamble into a controlled engineering decision.
Non-engineers can direct the work. When the complexity of a single change is compressed to the scale of one component, the unit of review shrinks with it: a person can review results component by component without holding the whole application in their head. That opens the door for people without an engineering background to direct AI coding themselves — for example, a product manager driving an entire product iteration.
Why FaaS deployment
Every component needs a dedicated backend service to feed it dynamic data, and FaaS is a natural home for that service.
I’ll say it outright: FaaS will be the most important piece of infrastructure in the AI era.
The core idea of FaaS is that the function is a first-class citizen: functions, rather than applications, are the unit of deployment, and each one is developed, deployed, and operated independently. This fits “the component is the smallest unit of delivery” like a glove — a component’s backend naturally is a function.
The one-to-one pairing buys a decisive simplification: when AI writes a component function, it doesn’t have to think about system-level reuse at all. There is no need to design generic interfaces for hypothetical future callers or accommodate unrelated use cases; the function only has to meet this component’s data needs. The narrower the responsibility, the easier it is for AI to generate the function directly from the component’s requirements.
To be clear: what this architecture demands of “FaaS” is a contract, not a particular product. Any function runtime that offers component-dedicated, independently deployable, programmatically invocable functions with environment isolation — public-cloud function compute, edge functions, or a self-hosted lightweight container runtime — can satisfy that contract (see §5 in Section 12).
Why duplication is allowed — reuse has moved
“Every component gets its own dedicated function, and components share no code” — at this point any trained engineer will wince: that violates DRY.
But DRY rests on an economic premise. The real cost of duplication was never “typing it twice”; it’s “when it changes, you change N places and might miss one.” Once AI pushes the marginal cost of “change N places” toward zero (one instruction updates every copy, and each copy is correctly understood within its own small context), what’s left of the cost of duplication is storage. Storage is free.
The wrong abstraction, meanwhile, costs more in the AI era, not less. An abstraction layer shared by five use cases is a context tax every agent pays on every task. One act of overengineering in the name of “possible future reuse” forces every later change to begin by understanding that design. Sandi Metz’s observation holds truer than ever: the wrong abstraction is far more expensive than duplication.
So the claim is not “don’t reuse” — it’s move reuse up:
- Don’t reuse code: components share implementation by copying, not by depending. A copy is yours; break it and you break only yourself.
- Reuse interfaces: business capabilities live in domain services and existing APIs; component functions provide a thin orchestration layer over them (the next section explains how AI finds the right interface).
- Reuse assets: visual consistency comes from design tokens, component templates, and scaffolding — reuse happens at generation time, not runtime.
Let reuse live at the interface and design-asset layers, not the code layer.
How component functions find dependencies: AIRS
Component functions don’t implement core business logic; they orchestrate existing capabilities — looking up a product, checking inventory, fetching coupons, placing an order. A company may expose thousands of these capabilities through HTTP / RPC interfaces in its domain services.
This creates a new problem in the AI era: how does an AI quickly and accurately find, among thousands of existing interfaces, the one this task actually needs? Documentation is scattered, descriptions go stale, and naming is inconsistent. Human engineers get by on tribal knowledge and word of mouth. AI has no tribe.
For this, the architecture defines AIRS — the Agentic Interface Retrieval Specification:
- Every interface is registered using a common metadata standard: a semantic description, request and response schemas, real invocation examples, an owner, an SLA, and a stability grade;
- A retrieval service lets an agent describe its data needs in natural language and returns ranked candidate interfaces with their full contracts;
- AIRS is development-time infrastructure, not runtime infrastructure: once code is generated and shipped, component functions still call target services directly over native HTTP / RPC. AIRS proxies no production traffic and introduces no runtime dependency.
MCP and AIRS address different questions. MCP explains how an agent calls tools at runtime; AIRS explains how an agent discovers existing interfaces while coding and generates native calls to them. They are complementary and operate at different stages of the software lifecycle.
AIRS’s full contract is in §6 of Section 12.
The supporting infrastructure: machine-operable by design
The chapters so far address AI’s coding problem. But coding may account for only 20% of a delivery cycle. For AI to take over delivery, human-dependent stages such as testing and deployment must also be exposed through interfaces that AI can operate independently.
A deployment API. The infrastructure layer must expose programmatic component deployment: frontend artifacts published to a CDN (content-addressed, immutable URLs), backend code deployed as functions, and deployment status that is queryable with a deterministic success-or-failure verdict. That last clause is not pedantry. Without a machine-decidable terminal state — a successful API call does not mean a successful deployment — an automated caller cannot reliably distinguish success, failure, and a deployment still in progress. An agent’s delivery loop will repeatedly fail at this boundary.
A simulator. For end-to-end self-testing, AI needs a simulator that can render the real page, load a given version of the page config and component code, inject a test identity and parameters, invoke functions in the chosen environment, and return screenshots, DOM structure, console logs, and network traces. The simulator serves two audiences at once: AI uses it for functional verification (Principle 7: the environment is the only source of truth), while humans use it to preview changes during review. Both see the same rendered result.
Observability. Every function invocation carries an end-to-end traceId; logs are programmatically queryable by traceId and time range. Every step in an agent’s debugging loop — call fails → query logs → locate the cause → fix → retry — must be available through an API.
An environment model. The infrastructure must provide environment isolation semantics (at minimum: dev / staging / production), and deployment scope and invocation allowlists must be two independent control planes. Permission to deploy automatically to several environments does not imply permission to invoke functions in all of them. Production releases must pass a human review gate.
A feature's journey from request to release
Let’s walk one request through the whole pipeline in a generic e-commerce setting. The request: “Add a ‘final price calculator’ component to the product detail page, showing the final price after all eligible coupons have been applied.”
- Understand the page. The agent reads the
product-detailpage config, sees the current component list, and decides where the new component slots in. - Create the component. The agent generates scaffolding according to the component standard (§1): a
price-calculator/directory with a frontend entry, a function entry, a component brief, and tests. - Find the interfaces. The agent queries AIRS for “coupons available to a user on a given product,” receives candidate interfaces with schemas and call examples, and snapshots the chosen contracts into the component directory to keep the context self-contained.
- Write the code. With everything in context, the agent writes the frontend and dedicated function — a few hundred lines in total — in one pass. The function orchestrates the coupon and pricing interfaces; the frontend renders with design tokens.
- Deploy to dev. The agent calls the deployment API to publish the frontend artifact and the function, then polls deployment status until it reaches a deterministic success state.
- Self-test in the simulator. The agent renders the real page as a test user, checks the screenshot and DOM, and verifies that the console is clean. When it finds a white screen in the “no coupons available” case, it adds a fallback, redeploys, and reruns the test successfully.
- Submit for human review. The deliverable = code diff + simulator preview link + self-test record. The human reviews the deliverable, not the process.
- Ship. On approval, publish the new page-config version; it’s live. If a rollback is ever needed, move the config pointer back one version — done in seconds.
In the entire journey, the human appears exactly once: step 7.
How roles and organizations change
This architecture abstracts away the product’s implementation details. By reducing AI’s cognitive load, it enables AI to handle coding, testing, and deployment independently. Human attention moves from how to build it to what to build — from implementation details to the product and its value to users.
The role an organization needs changes too. We call it the AI product full-stack engineer:
- Product-minded, with end-to-end ownership of product iteration;
- Technically literate enough to diagnose and resolve the occasional mistakes AI makes;
- Focused on setting direction, reviewing output, and managing risk — shifting from producing solutions to reviewing them.
The organization no longer needs to divide every product area among narrowly defined roles (ops, product, frontend, backend, algorithms, QA, BI, and SRE, each in its own lane). A single AI product full-stack engineer can own one product area end to end, pushing it forward with a fleet of agents. The review process can evolve as trust grows: review every deliverable at first, then, as quality data accumulates, review only the critical decisions.
Drawbacks, objections, and responses
An honest manifesto has to face its own costs.
1. FaaS costs go up. True. One-to-one functions forgo reuse and are deployed independently, so infrastructure costs are higher than with shared services. Our response: this trades higher infrastructure spending for lower labor costs and faster iteration, and the direction of those price curves is clear. Per-function elastic scaling, including scaling to zero when idle, also keeps the cost of most long-tail components under control.
2. Humans no longer know the implementation details — how do you control risk? True, and it deserves to be taken seriously. Humans review only the final deliverable; everything in between is AI’s work, and defects can lurk in branches no human ever examined. Our answer is defense in depth: deterministic tests + real-environment simulator verification (not LLM self-assessment) + human review gates + component-level blast radius + config rollback in seconds + end-to-end observability. Trust, but verify.
3. If every component is built independently, won’t the look and feel drift? It will, if you do nothing. That is why reuse moves up (Section 6): design tokens and component templates constrain the visuals at generation time, the page framework standardizes shell behavior, and the standard’s deterministic checks (lint-grade rules) catch violations before deployment.
4. Don’t N functions create N times as much attack surface? The function count does go up, but each function has a narrower permission scope. The safeguards are a uniform request envelope with gateway authentication, least privilege per function, static security scanning built into the deployment gate (the scanners can be agents too), and caller audits for AIRS-registered interfaces.
5. Isn’t this just low-code? No. Low-code constrains people: visual assembly instead of code, expressiveness traded away for accessibility. Here the code is still fully expressive code — it’s just written by AI. What’s constrained is the boundary (how small a component is, what it may depend on), not the expression (what logic you can write).
6. Isn’t this just micro-frontends / server-driven UI? The mechanisms overlap (runtime assembly, config-driven), but the objective functions differ. Micro-frontends solve multi-team parallelism and incremental migration, so they tolerate huge sub-applications; SDUI solves release agility. This architecture addresses AI’s cognitive load, so it requires small components, one-to-one frontend-backend pairing, and zero code sharing. The first two approaches do not need those constraints; this architecture does.
7. What about complex business logic and cross-component interaction? The architecture has a defined scope, described below. Cross-component interaction goes through the page framework’s restricted event bus (broadcast semantics, serializable payloads, neither side assuming the other exists); direct dependencies between components are forbidden. Complex business logic should not live in component functions in the first place. It belongs in domain services; component functions only orchestrate.
8. If the Provider interfaces (§5) are yours to implement, what’s the MCP Server in the reference implementation for? They sit on opposite sides of dependency inversion and serve different consumers. The Provider interfaces are an SPI facing the infrastructure, answering “who implements deploy, invoke, render”; the MCP Server is an API facing coding agents, answering “how does an agent use those actions.” It orchestrates the delivery workflow above the Providers into a handful of stable tools (e.g. deploy_component = publish frontend assets → deploy function → poll deployment status to a deterministic terminal state → update page config), and implements no infrastructure of its own. Two consequences follow. First, §5’s process discipline (such as MUST NOT infer deployment success from call success) is baked into the tool implementations, so no team has to reinvent it in agent prompts. Second, agent-side tool signatures and workflows don’t change with the infrastructure — develop locally against the reference implementation’s built-in local Provider, swap in your own adapters for production, and the agent never notices.
Applicability. The architecture is best suited to consumer-facing display and marketing pages, campaigns, growth experiments, content pages, dashboards, and admin consoles — anywhere with a clear page structure, frequent iteration, and a high payoff from experimentation. Use it with care for core domains that require strict transactional consistency (trading, accounting, inventory deduction), interaction-heavy single-page tools (editors, IDEs), and extremely performance-sensitive paths. We are not proposing that you use it to rewrite your core domain. It is the fast-evolving skin around the core, and that skin is where the vast majority of product iteration happens.
The architecture standard (v0.1 draft)
The normative text is maintained in the contextile/spec repository; this section provides a reader-friendly snapshot. This section is normative. The key words MUST, MUST NOT, SHOULD, and MAY are to be interpreted as described in RFC 2119. The full specification evolves through an RFC process in its own repository. A team may adopt only part of the standard; see §8 for conformance levels.
§0Terminology
| Term | Definition |
|---|---|
| Page | A runtime assembly identified by a pageId, whose structure is declared by a page config |
| Component | The smallest unit of delivery = frontend artifact + dedicated function + manifest |
| Component Function | A backend function dedicated to exactly one component, bound 1:1 |
| Page Config | A versioned JSON document declaring a page’s component list and assembly parameters |
| Renderer | The shared runtime that resolves page config, loads components, orchestrates data, and handles fallbacks |
| Provider | The infrastructure adapter interfaces (four kinds: config, function, asset, simulator) |
| Envelope | The standard request/response format between the renderer and component functions |
§1The component standard
1.1 Directory layout. A component MUST be a self-contained directory:
price-calculator/
├── component.json # component manifest (see 1.2)
├── CLAUDE.md # agent-facing component brief (see §7)
├── frontend/
│ ├── index.jsx # frontend entry, ESM default export
│ └── styles.css # scoped styles
├── backend/
│ ├── handler.ts # the component's dedicated function entry
│ └── apis.md # contract snapshots of the external interfaces
│ # this function depends on (archived AIRS results)
└── tests/
├── frontend.test.jsx
└── backend.test.ts
1.2 Component manifest. component.json MUST exist:
{
"componentId": "price-calculator",
"name": "Final price calculator",
"owner": "example-team",
"frontend": { "entry": "frontend/index.jsx" },
"backend": { "entry": "backend/handler.ts", "runtime": "node20" },
"budget": { "maxSourceLines": 3000 },
"test": { "command": "npm test" }
}
1.3 Size budget. A component’s combined frontend and backend source SHOULD be ≤ 3,000 lines. The number is empirical; the hard rule behind it: with the component’s full source loaded into a mainstream model’s context window, at least half the window MUST remain free for reasoning, conversation, and tool output. A component over budget MUST be split into multiple components.
1.4 Isolation.
- A component MUST NOT import any code from another component;
- A component MUST NOT read or write another component’s DOM or global state;
- Inter-component communication MUST go through the renderer’s event bus (§3.4);
- Styles MUST be scoped; bare element selectors and modifications to
:rootare forbidden; - The base runtime (react / react-dom / the design system’s base package) is provided by the renderer via import map; a component MAY depend on that allowlist, and every other dependency MUST be bundled into the component’s own artifact.
§2The page config contract
A page config is a versioned JSON document:
{
"pageId": "product-detail",
"version": "42",
"meta": { "title": "Product detail" },
"components": [
{
"componentId": "price-calculator",
"frontend": {
"format": "esm",
"url": "https://assets.example.com/components/price-calculator/42/index.js",
"integrity": "sha384-…"
},
"function": {
"name": "shop.product-detail.price-calculator",
"timeoutMs": 3000,
"required": false
},
"props": { "showDiscounts": true },
"fallback": "hide"
}
]
}
Rules:
- Config versions MUST be immutable: publishing = creating a new version, rolling back = moving the active-version pointer (§5.1);
frontend.urlMUST be content-addressed (hash in the path or filename) and SHOULD carry a subresource integrity hash;fallbackMUST be one ofhide | skeleton | message, declaring how the component degrades on failure;- When a
required: truecomponent fails, the renderer MUST execute the page-level degradation policy; a failingrequired: falsecomponent MUST NOT block other components from rendering; - The renderer MUST issue all components’ initial data fetches in parallel and stream-render per component.
§3The data contract
3.1 Request envelope (renderer → component function):
{
"page": { "pageId": "product-detail", "version": "42", "query": { "productId": "1234" } },
"component": { "componentId": "price-calculator", "props": { "showDiscounts": true } },
"context": { "traceId": "ctx-9f3c…", "user": { "id": "u_888" }, "device": "mobile", "locale": "en-US" },
"action": "initial",
"params": {}
}
action = "initial" marks the initial page-load fetch; later interactive fetches use component-defined action names, with routing handled inside the function.
3.2 Response envelope (component function → renderer):
{
"success": true,
"data": { "finalPrice": 128.0, "discounts": ["…"] },
"errorCode": null,
"errorMessage": null,
"traceId": "ctx-9f3c…",
"costMs": 87
}
On failure, success=false; errorCode MUST be machine-decidable; errorMessage SHOULD be human-readable and actionable.
3.3 The frontend component contract. The frontend entry MUST be an ESM default-exported component with this signature:
export default function PriceCalculator({ data, props, context, actions }) {
// data: dynamic data returned by the function (prefetched in parallel for the initial page load)
// props: static configuration from the page config
// context: page context (user, device, locale — read-only)
// actions: restricted capabilities injected by the framework:
// actions.invoke(action, params) → call this component's dedicated function (follow-up fetches)
// actions.emit(event, payload) → broadcast on the page event bus
// actions.on(event, handler) → subscribe to page events
// actions.track(event, payload) → analytics
return <div>…</div>;
}
actions.invoke MUST only reach this component’s own dedicated function — cross-component data fetching is inexpressible at the protocol level.
3.4 The event bus. Event names MUST carry the component’s namespace; payloads MUST be JSON-serializable; the bus uses broadcast semantics with no delivery guarantee; a component MUST NOT assume any given event has a subscriber or a publisher.
§4The component function contract
- A function MUST be stateless; instances share no in-memory state;
- A function MUST serve exactly one component (1:1) and MUST NOT be shared across components — logic that needs sharing belongs in a domain service;
- A function MUST accept the §3.1 request envelope and return the §3.2 response envelope over HTTP POST + JSON (any language satisfying the contract qualifies);
- Function names SHOULD follow
{namespace}.{pageId}.{componentId}and MUST be globally unique; - A function MUST respond within its
timeoutMsbudget; reads SHOULD be idempotent; - Logs MUST carry the traceId from the request envelope;
- Calls to domain services MUST go directly over native HTTP / RPC (never through AIRS or any other development-time facility).
§5The Provider interface standard
Infrastructure dependencies are inverted into four Provider interfaces. By implementing these four interfaces, any company can host this architecture on its own infrastructure. The reference implementation includes a batteries-included local implementation (config in files, functions as local processes, assets served locally) for development and evaluation.
5.1 PageConfigProvider
interface PageConfigProvider {
getPage(pageId: string, version?: string): Promise<PageConfig>; // defaults to the active version
publishPage(config: PageConfig): Promise<{ version: string }>; // versions are immutable
listVersions(pageId: string): Promise<VersionMeta[]>;
setActiveVersion(pageId: string, version: string): Promise<void>; // publish/rollback = move the pointer
}
5.2 FunctionProvider
interface FunctionProvider {
deploy(bundle: ComponentBundle, env: Env): Promise<{ deployId: string }>;
status(deployId: string): Promise<DeployStatus>; // terminal states MUST be deterministic; FAILED MUST carry a machine-readable reason
invoke(fn: string, envelope: RequestEnvelope, env: Env): Promise<ResponseEnvelope>;
logs(fn: string, filter: { traceId?: string; range?: TimeRange }): Promise<LogEntry[]>;
}
Environment semantics: a Provider MUST offer environment isolation (at minimum DEV / STAGING / PROD); deployment scope and the invocation allowlist MUST be two independent settings; status MUST give a deterministic terminal-state verdict and MUST NOT require callers to infer “deployment succeeded” from “the API call succeeded.”
5.3 AssetPublisher
interface AssetPublisher {
publish(files: FileMap): Promise<{ urls: Record<string, string> }>;
// URLs MUST be content-addressed and immutable; SHOULD return integrity hashes
}
5.4 SimulatorProvider
interface SimulatorProvider {
render(input: {
pageId: string;
overrides?: PageConfigPatch; // overlay unpublished draft config
context: RenderContext; // inject test identity and parameters
env: Env; // environment for function calls (subject to the invocation allowlist)
}): Promise<{
screenshot: Bytes;
dom: string;
console: ConsoleMessage[];
network: NetworkRecord[];
errors: RenderError[];
}>;
}
The simulator MUST use the same renderer used in production and MUST distinguish two readiness signals — “data returned” and “view visible”: a white screen with a 200 response is not ready.
§6The AIRS retrieval standard
6.1 Interface metadata. Every registered interface MUST provide:
The record below is a fully synthetic example used only to illustrate the contract shape:
{
"name": "demo.discount.list-eligible",
"protocol": "http",
"endpoint": "POST /example-api/discounts/eligible",
"semantic": "Lists the discounts a specified user is eligible to apply to a specified product in a synthetic example",
"tags": ["demo", "discount", "eligibility"],
"request": { "$schema": "…", "properties": { "userId": {}, "productId": {} } },
"response": { "$schema": "…", "properties": { "discounts": {} } },
"examples": [ { "request": { "userId": "demo-user", "productId": "demo-product" }, "response": { "discounts": [] } } ],
"owner": "example-team",
"stability": "stable",
"sla": { "p99Ms": 150, "availability": "99.95%" },
"deprecated": false
}
6.2 Retrieval protocol.
POST /airs/search
{ "query": "which discounts can this demo user apply to this demo product", "topK": 5 }
→ returns candidate interfaces ranked by relevance, with full metadata
6.3 Discipline.
- AIRS MUST be used at development time only; generated code MUST call native protocols directly, and production traffic MUST NOT flow through the retrieval service;
- Agents SHOULD snapshot the chosen interfaces’ contracts into the component directory (
backend/apis.md), keeping the component’s context self-contained; - The
semanticfield is the primary determinant of retrieval quality and MUST be written in terms of usage scenarios, not implementation details.
§7The agent workspace standard
7.1 Component brief. Every component MUST include an agent-facing brief (CLAUDE.md / AGENTS.md) that MUST cover: the component’s responsibility and business context, a summary of its data contracts, the interfaces it depends on, test and verification commands, and explicit modification boundaries.
7.2 Self-contained context. Everything needed to understand and modify a component MUST live inside the component directory — including contract snapshots of the external interfaces it depends on. Completing a component task MUST NOT require the agent to read business code outside the component directory.
7.3 Modification boundary. One agent task MUST modify exactly one component directory; a requirement spanning components MUST be split into multiple tasks (which is also the safety precondition for multi-agent parallelism).
7.4 Verification loop. A component SHOULD provide a deterministic test command (test.command in component.json); before release, a real render check MUST pass in the simulator (screenshot + DOM + clean console). Verification ends in the environment, not in the model’s assessment of itself.
§8Conformance levels
| Level | Name | Requirements | What it enables |
|---|---|---|---|
| L1 | Rendering conformance | §2 + §3 | Pages assemble and render from config; the backend can be any existing service |
| L2 | Delivery conformance | L1 + §1 + §4 + §5 | Agents can deploy components end to end through the Provider interfaces |
| L3 | AI-native conformance | L2 + §6 + §7 (incl. simulator) | Agents can independently close the loop: develop → deploy → self-test → submit for review |
A conformance test suite (TCK) will ship with the reference implementation: pass the suite, claim the level.
From component delivery to product iteration
The architecture standard explains how an agent can deliver a component within explicit boundaries. Applying that capability to product iteration also requires a few general-purpose delivery mechanisms: machine-readable requirements and delivery state; auditable human review for consequential decisions; isolated execution, deterministic verification, and rollback for each change; and feedback from key product metrics to inform later decisions.
These mechanisms do not prescribe a particular platform architecture. They can be assembled from existing CI/CD, project-management, agent-tooling, and observability systems or provided by an integrated platform. The architecture standard defines component-delivery boundaries and interfaces; it does not depend on any particular orchestration product. Any team can implement the same develop → review → ship loop on L2/L3-conformant infrastructure.
Let's define the architecture of the AI era
For fifty years, every generation of architecture — structured programming, object orientation, SOA, microservices — has optimized for the same constraint: the coordination cost of human engineers. That constraint is beginning to loosen. This is a once-in-fifty-years opportunity to rethink architecture from first principles.
This manifesto’s answer is to design architecture for the cognitive limits of AI: components small enough to fit in context, boundaries strong enough to contain the blast radius, infrastructure accessible enough for machines to operate directly, and verification grounded in real environments that get the final word.
The standard will evolve in the open. Use these entry points to read, try, or contribute to the project:
- Project website — contextile.dev: read the manifesto in English or Chinese, follow the quickstart, and find the project’s latest entry points;
- contextile/contextile: the reference implementation, including the renderer SDK, all-in-one local runtime, CLI, and MCP Server for delivering Contextile components end to end;
- contextile/contextile-bench: the public benchmark, with cross-architecture experiments at multiple scales, tasks, raw scores, correction ledger, and analysis code for replication and extension;
- contextile/manifesto: the English and Chinese source text of this manifesto, its figures, and version history;
- contextile/spec: the normative Contextile standard, including specification chapters, JSON Schemas, type definitions, and the RFC process.
Provider adapters will expand through a combination of official examples and community implementations.
If you also believe architecture deserves to be redesigned for AI, join at whatever layer suits you: implement a Provider adapter, submit an RFC, or simply pick one page in one of your products and try it.
You set the direction. The product grows itself.
— Asher Chai (@asher-chai)