The canonical architectural diagram of Remy explained: how annotated prose compiles into methods, tables, and roles, then projects onto eight interfaces.
At a Glance #
Three layers: Spec (annotated prose) → Backend contract (methods, tables, roles) → Interfaces (web, API, Discord, Telegram, cron, webhook, email, MCP, agent)The spec is source: Lives insrc/app.md
, written in MSFM (MindStudio-Flavored Markdown). The application’s intent, domain knowledge, and rules.The backend is compiled output: Lives indist/methods/
, TypeScript methods and table definitions generated from the spec. In the same way.js
is a compiled derivation of.ts
, the backend code is a compiled derivation of the spec.Interfaces are projections: Eight ways to interact with the same backend contract. One method powers all of them.Two SDKs enforce the contract:@mindstudio-ai/agent
(backend) and@mindstudio-ai/interface
(frontend). Type-safe, sandboxed, managed.Deploy is a compile step:git push
→ parse spec → generate code → build interfaces → apply schema changes → go live.
Remy is a product agent that compiles annotated markdown into a full-stack app — backend, database, frontend, auth, tests, and deployment — in a single step. The three-layer model is how that compilation works.
What Are the Three Layers? #
Every Remy app has the same structure:
Layer 1: The Spec. A natural language document describing what the app does. Written in MSFM (MindStudio-Flavored Markdown), it captures the domain, the rules, the workflows. This is the source of truth. An AI agent reads the spec and generates the code, or you write the code directly. Either way, the spec is the application; the code is a derivation.
Layer 2: The Backend Contract. Methods, tables, and roles. Methods are TypeScript functions that implement the logic. Tables define the data model. Roles define who can do what. This lives in dist/
. In the same way .js
is a compiled derivation of .ts
, the backend code is a compiled derivation of the spec.
Layer 3: Interfaces. Ways for users to interact with the contract. A web app, a REST API, a Discord bot, a Telegram bot, a cron job, a webhook, an email trigger, an MCP tool server, a conversational agent. The same methods power all of them. Interfaces can be as complex and polished as you want, but they’re always safe, because the backend contract is where anything real happens. The interface can’t break business logic or corrupt data.
The directory structure makes this visible:
my-app/
mindstudio.json ← the manifest (declares everything)
src/
app.md ← Layer 1: the spec (the application)
references/ ← supporting material
dist/
methods/ ← Layer 2: backend contract (compiled from spec)
src/*.ts methods
src/tables/*.ts table definitions
.scenarios/ seed scripts for testing
interfaces/ ← Layer 3: projections of the contract
web/ React SPA
api/ REST API config
discord/ bot config
...
The src/
directory is authored source. Natural language specs, brand guidelines, reference materials. No code. The dist/
directory is compiled output. TypeScript methods, React frontends, JSON configs. Generated from src/
by an AI agent (or written directly).
How Does Layer 1 Work? (The Spec as Source) #
The spec is the most important file in your project. It lives in src/app.md
and describes what the app does: the data model, the business rules, the workflows, the edge cases.
Here’s a fragment from a vendor approval app:
---
name: Procure-to-Pay (P2P)
description: Procure-to-Pay process for all domestic (U.S.) spend.
version: 1
---
When requesting a new vendor, there are three areas of review:
governance, legal, and accounting, with approvals flowing in that order.
~~~
The spec says "three areas" but lists four names. From the process flowchart, it is three sequential stages:
1. Governance, Risk & Compliance (GRC), one combined stage
2. Legal
3. Accounts Payable (AP)
These are sequential. Each stage must complete before the next is notified. If any stage rejects, the entire request is rejected.
~~~
All invoices can be sent to the [Accounts Payable]{The AP team in
this context refers to the internal accounts payable department, not
a vendor's AP. Only users with the "ap" or "admin" role can process
invoices.} team for processing against the PO.
The spec is written in MSFM (MindStudio-Flavored Markdown), which extends standard Markdown with two primitives:
Block annotations(fenced with~~~
) attach to the content immediately above them. They clarify ambiguities, pin down edge cases, specify data representations.Inline annotations([text]{content}
) attach to a specific word or phrase. Good for definitions, units, and clarifications.
Annotations are just more context. They’re not typed or structured. They can be a word, a paragraph, or a code snippet. Each annotation makes compilation more deterministic.
A spec with good annotations compiles the same way every time. When you want to change the app’s behavior, update the spec and the agent regenerates the code.
Other agents ship a demo. Remy ships an app. #
Real backend. Real database. Real auth. Real plumbing. Remy has it all.
You can also write code directly in dist/
without a spec. That works too. But the spec is what makes the project maintainable over time. Code shows what the app does; the spec captures why.
What Is the Backend Contract? (Layer 2: Methods, Tables, Roles) #
The backend contract is the core of the app: the methods and data model. It lives in dist/methods/
.
Methods
A method is a named async function that runs on the platform. It’s the universal unit of backend logic. Every interface (web, API, Discord, cron, webhook) is just a different way to invoke a method.
One file per method, one named export:
// dist/methods/src/submitVendorRequest.ts
import { db, auth } from '@mindstudio-ai/agent';
import { Vendors } from './tables/vendors';
export async function submitVendorRequest(input: {
name: string;
contactEmail: string;
taxId: string;
}) {
auth.requireRole('requester');
const vendor = await Vendors.push({
name: input.name,
contactEmail: input.contactEmail,
taxId: input.taxId,
status: 'pending',
requestedBy: auth.userId,
});
return { vendorId: vendor.id, status: vendor.status };
}
Methods receive a single input
parameter (an object) and return an object. Both must be JSON-serializable. Methods run in isolated sandboxes with npm packages pre-installed. No servers to manage.
The @mindstudio-ai/agent
SDK provides db
and auth
namespaces that just work, plus access to 200+ AI models, 1,000+ integrations, and platform actions like file uploads and web scraping.
Tables
Each table is a TypeScript file with a typed interface and a defineTable<T>()
call:
import { db } from '@mindstudio-ai/agent';
interface Vendor {
name: string;
contactEmail: string;
status: 'pending' | 'approved' | 'rejected';
taxId: string;
paymentTerms?: string;
}
export const Vendors = db.defineTable<Vendor>('vendors');
One file per table, one export per file. The export name is what you reference in mindstudio.json
and import in methods.
The platform provides managed SQL databases with typed schemas. No connection strings, no migrations to run manually. Push a schema change and the platform diffs it, clones the database, applies DDL, and promotes atomically. In development, reset to live data or truncate to empty tables with a single command.
Every table gets system columns automatically (id
, created_at
, updated_at
, last_updated_by
). You don’t define them; they’re added by the platform and maintained by SQLite triggers.
Roles
Roles are declared in mindstudio.json
:
{
"roles": [
{ "id": "requester", "name": "Requester", "description": "Can submit vendor requests and purchase orders." },
{ "id": "approver", "name": "Approver", "description": "Reviews and approves purchase orders." },
{ "id": "admin", "name": "Administrator", "description": "Full access to all app functions." },
{ "id": "ap", "name": "Accounts Payable", "description": "Processes invoices and payments." }
]
}
Roles are synced to the platform on deploy. Users are assigned to roles in the editor. In your methods, call auth.requireRole('admin')
and the platform handles sessions, tokens, and user resolution.
The backend contract is what the platform builds and runs. It’s real TypeScript, real SQL, real npm packages. You own the output. No vendor lock-in at the code layer.
How Do Interfaces Work? (Layer 3: Eight Ways to Invoke the Same Methods) #
Interfaces are how users interact with your app. The same backend methods power all of them. A web frontend, a Discord bot, and a cron job can all invoke the same backend logic.
| Interface | What it does |
|---|---|
| Web | React/Vite SPA hosted on CDN |
| API | REST endpoint with API key auth |
| Discord | Slash commands that invoke methods |
| Telegram | Bot commands + message handling |
| Cron | Scheduled method execution |
| Webhook | Inbound HTTP → method invocation |
| Inbound email → method invocation | |
| MCP | AI tool server (methods as tools) |
| Agent | Conversational interface with LLM orchestration |
Your methods don’t know or care which interface invoked them. The same submitVendorRequest
method works whether it’s called from a React form, a Discord slash command, or a Stripe webhook.
Interfaces can be as complex and polished as you want, but they’re always safe, because the backend is where anything real happens. The interface can’t break business logic or corrupt data.
Web Interface
A full web application. The scaffold starts as Vite + React, but any framework with a build step works.
The frontend SDK provides typed RPC to backend methods:
import { createClient } from '@mindstudio-ai/interface';
const api = createClient<{
submitVendor(input: { name: string }): Promise<{ vendorId: string }>;
listVendors(): Promise<{ vendors: Vendor[] }>;
}>();
const { vendorId } = await api.submitVendor({ name: 'Acme' });
const { vendors } = await api.listVendors();
On git push
, the platform runs npm install && npm run build
in the web directory and hosts the output on CDN. Zero configuration in your code. The platform injects connection details automatically.
API Interface
Exposes selected methods as REST endpoints with clean URLs and HTTP methods — for external consumers (other services, mobile apps, integrations).
Routes are declared in a spec file (src/interfaces/api.md
) and compiled into structured config (dist/interfaces/api/api.json
) the platform reads for routing and OpenAPI generation.
Example:
curl -X POST https://{app-subdomain}.mindstudio.ai/_/api/vendors \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Acme", "contactEmail": "billing@acme.com" }'
Bot Interfaces (Discord, Telegram)
Slash commands and message handling that invoke methods. Commands are synced to the platform on deploy. Same backend logic, different modality.
Scheduled and Event-Driven Interfaces (Cron, Webhook, Email)
Cron jobs run on a schedule. Webhooks accept inbound HTTP. Email triggers invoke methods when mail arrives at a registered address. All three are just different ways to invoke the same backend methods.
AI Interfaces (MCP, Agent)
MCP exposes methods as tools for external AI agents (Claude Desktop, Cursor, etc.). The agent interface IS the agent — it has its own personality, system prompt, and model config, and orchestrates tool calls against the app’s methods internally.
For a comparison of how different AI models approach agentic coding workflows, see Qwen 3.6 Plus vs Claude Opus 4.6 on agentic coding.
How Do the Layers Connect? (The Two SDKs) #
The backend contract is enforced by two SDKs:
Backend: @mindstudio-ai/agent
Used inside methods. Provides database access, auth, and platform capabilities:
import { db, auth } from '@mindstudio-ai/agent';
import { Vendors } from './tables/vendors';
export async function approveVendor(input: { vendorId: string }) {
auth.requireRole('admin');
const vendor = await Vendors.update(input.vendorId, {
status: 'approved',
});
return { vendor };
}
Frontend: @mindstudio-ai/interface
Used in web interfaces. Typed RPC to backend methods:
import { createClient } from '@mindstudio-ai/interface';
const api = createClient<{
approveVendor(input: { vendorId: string }): Promise<{ vendor: Vendor }>;
}>();
const { vendor } = await api.approveVendor({ vendorId: '...' });
The SDKs are the contract. Methods use @mindstudio-ai/agent
to access the database and auth. Interfaces use @mindstudio-ai/interface
to invoke methods. The platform handles the rest: sandboxed execution, database management, auth sessions, deployment.
Remy doesn't write the code. It manages the agents who do. #
Remy runs the project. The specialists do the work. You work with the PM, not the implementers.
For a deeper look at how AI models are evolving to support this kind of architecture, see Gemma 4 vs Qwen 3.5 on open-weight agentic workflows.
How Does Compilation Work? (From Spec to Live App) #
When you push to git, the platform compiles the spec into a live app:
Parse manifest— readmindstudio.json
from the commitCreate release— record in Postgres with statusbuilding
Mirror files— copy repo files to S3 for instant editor access** Compile methods**— esbuild bundles each method into a single JS file, extracts npm package dependencies** Compile interfaces**— build the web interface (npm install && npm run build
), generate configs for API/Discord/Telegram/cron/etc.Parse table schemas— TypeScript AST → column definitions, diff against live database** Compute pending effects**— roles diff, cron diff, bot command diffs, webhook/email diffs, table DDL** Apply**— create/update roles, sync bot commands, apply DDL to a staging database copy, swap the live pointer
The build log captures every step with timing. On failure, the error and context are captured. Failed releases never affect the live release.
Schema changes are always applied to a clone, never to the live database directly. If the DDL fails, the live database is untouched and the release is marked failed
.
Databases are keyed by release ID. Each release gets its own database copy. Rollback is safe because the previous release’s database still exists.
Why Does the Three-Layer Model Matter? #
The three-layer model is what makes Remy a product agent, not a code generator.
The spec is the source of truth. When AI models improve, you recompile the same spec into a better app. When requirements change, you update the spec and regenerate the code. The spec is what makes the project maintainable over time.
The backend contract is portable. It’s real TypeScript, real SQL, real npm packages. You own the output. No vendor lock-in at the code layer. Migrating off the platform is a download and a re-pointing of your data-access layer; it’s not a months-long data extraction project.
Interfaces are projections. The same backend methods power all of them. You can add a Discord bot, a cron job, or a webhook without touching the core logic. Interfaces can be as complex and polished as you want, but they’re always safe, because the backend is where anything real happens.
The three-layer model is the architectural reason Remy can ship a full-stack app from a spec. It’s not magic. It’s a compiler.
For more on how this compares to other AI coding approaches, see Claude Code’s three-layer memory architecture and Gemma 4 31B vs Qwen 3.5 for agentic workflows.
FAQ #
What is MSFM?
MSFM (MindStudio-Flavored Markdown) is the format for writing specs. It extends standard Markdown with block annotations (~~~...~~~
) and inline annotations ([text]{content}
) that attach precision to prose. A spec with good annotations compiles the same way every time.
Can I write code directly without a spec?
Yes. The dist/
directory is real code. You can write methods, tables, and interfaces directly in TypeScript and React without ever touching src/app.md
. The spec is optional. But the spec is what makes the project maintainable over time and what lets you regenerate the app when AI models improve.
What happens when I edit the compiled code?
The spec is the source of truth. If you regenerate from the spec, your hand-edits in dist/
are overwritten. The right workflow is: update the spec to capture the change, then recompile. If you want to own the code permanently, stop regenerating from the spec and treat dist/
as your codebase.
How does the database work?
Each app gets its own serverless SQL database. The platform handles durability, sync, and schema migrations automatically.
Can I use an external database?
Remy’s managed database is optimized for the spec-driven compilation workflow. If you need a different database architecture, use a different tool for that workload.
What’s the difference between a product agent and a coding agent?
Product agents and coding agents are different categories for different jobs. Coding agents edit existing codebases. Product agents compile specs into complete applications. They don’t compose in the same workflow.
How do I deploy a Remy app?
git push origin main
. The platform builds and deploys automatically. Push to a feature branch for a preview deployment. Rollback is a git revert.
What is Remy?
Remy is a product agent that compiles annotated markdown into a full-stack app — backend, database, frontend, auth, tests, and deployment — in a single step. Built by Wooster Labs on the MindStudio platform substrate. See goremy.ai.
Start Building with the Three-Layer Model #
The three-layer model is the architectural foundation of every Remy app. Spec → backend contract → interfaces. Annotated prose → TypeScript methods and tables → eight ways to interact with the same logic.
It’s not magic. It’s a compiler. And it’s how you ship a full-stack app from a conversation.