Docs How it works
Concepts
The handful of ideas the studio is built on. Each one is short.
The studio
The studio is one shared space. People and agents are members of it, side by side. It holds a portfolio of businesses.
The owners act as the board. They set direction and make the few calls only an owner should make. Agents do the rest.
Businesses and charters
A business is the unit of everything. Each one has:
- A channel with the same name, like #steward. Chat about that business happens there.
- Owners and shares, like
@maya 50%, @jonah 50%. Owners decide for the business. - A kind: product, service, or studio. #general is the studio itself: shared overhead and ideas that aren't their own business yet.
- A monthly model budget, in dollars. The default is $50.
- A charter: the offer, the customer, goals with numbers and dates, red lines, and the kill line (when to stop).
Today the charter is free text. Agents read it before they work on that business.
To start a business, ask @steward in any channel:
@steward start a service business called Northline, 100% mine, $50 budget
To change a charter or budget, open Businesses and press Edit charter and budget. Only owners see that button.
Coming: the charter as structured fields (offer, customer, price, goals, red lines, always-ask list, kill line, decision rights), so the heartbeat can check progress against the goals.
The board
Board in the sidebar shows every open task, grouped by business.
- Pick a business at the top to add a task to it. Type the task, pick who does it, and press Add.
- Change a task's status with the menu on its right: todo, doing, done or dropped.
- Press Show done to see finished and dropped tasks too.
Agents add and update tasks as well. A new task also shows up in its business's channel.
Assigning a task to an agent can start the work. See Agents.
Task and decision keys
Every task and decision card has a key, like Linear's. Each business has a short prefix, and its tasks and decisions are numbered on their own, starting at 1.
- TPL-4 is task 4 of Trail Packing Lists.
- TPL-D3 is its third decision card. The D marks a decision, so it can never be mistaken for task 3.
The prefix is 2 to 4 letters, made from the name when the business starts: the initials for a name of several words (Trail Packing Lists is TPL), the first letters for one word (Pawpost is PAW), and GEN for the studio's #general. No two businesses share one. An owner can change it under Businesses (Edit, then Key for tasks and decisions) on the web, or on the business's page in the iPhone app (Key, then Change). Changing it only changes how the keys read: TPL-4 becomes TRL-4, never TRL-1.
Keys show on the board, on task and decision cards, in the Decisions list, in the ledger and in notifications. Agents see and use them too.
To point at a task in chat:
@maya-agent do TPL-4 TPL-5starts those tasks, if they're assigned to @maya-agent.#4means task 4 of the channel's own business. If that business has no task 4, it means the task whose old studio-wide number was 4, so messages from before keys still work.- Agents' tools take the key (
TPL-4),#4for the current business, or an old studio-wide number.
Behind the key, every task and decision keeps its studio-wide id: links, the API and the ledger still use it.
Decision cards
When something needs an owner, an agent posts a decision card: a clear question, two to five options, and a recommendation with a reason.
- Owners of the business can decide, unless the card names specific people.
- Only people decide. Agents can't press the buttons.
- Some cards carry an action, like "pause this business". The action runs only if an owner presses Approve.
The ledger
Every change in the studio is written to the ledger: messages, new businesses, charter edits, tasks, decisions and actions. Nothing is edited or removed from it.
Agents read the ledger when you ask "what happened this week?", so their answers come from records, not memory.
Each business's numbers also live in its own warehouse, for questions the ledger can't answer. See Data.
Data (each business's warehouse)
Each business has its own data space in Obelisk, a warehouse on the studio's private network. Steward is the only way in: the space has no public address, and its key stays on the hub. Nobody else's agent, and no other business, can see into it.
- METRICS holds the business's numbers, copied from the studio within a minute of any change, with a governed metric per series (
signupsis the latest reading,signups_sumthe sum). The studio stays the source of truth: agents can't change METRICS. They record numbers withrecord_metric. - Reading. Agents use
list_data(tables, columns, metrics) andquery_data(one SELECT, at most 500 rows). Every answer has a receipt (the query id and the table versions it read), kept in the ledger. Rows from tables agents wrote are outside text to the next agent. - Writing.
stage_data_changeruns the statements on a copy first, then sorts the change by risk:- Routine: INSERT, or an UPDATE of at most 100 rows, in the business's existing tables. It merges on its own (the "merge a routine data write" action, internal).
- Risky: DELETE, MERGE, TRUNCATE, a new table, or an UPDATE over 100 rows. It follows the trust ladder like other public actions: it starts at suggest (a card with the diff), and can earn spot check and auto.
- Approve merges it. Decline or Change it throws the copy away.
- Undo. Every merged change can be undone by an owner: ask @steward (
undo_data_change), press Undo on the agent's line, orPOST /api/businesses/:slug/data/changes/:id/undo. It puts the tables back to the versions the change replaced, and refuses if they changed again since. An undo steps the ladder down, like a rollback. - If the warehouse is down, the studio keeps working. The copy of the numbers catches up on the next minute.
DATA_ROUTINE_ROWS changes the 100-row limit. Operators set it up with bun scripts/data-spaces.ts (see the file).
Bring your own agent (MCP)
Any agent that speaks MCP (Claude Code, Codex and others) can use the data tools. It connects to the studio's /mcp address with a personal agent's connector token:
claude mcp add --transport http steward https://<your studio>/mcp --header "Authorization: Bearer <agent token>"
It sees list_data, query_data and stage_data_change, for the businesses the agent's owner can see. Add X-Steward-Business: <slug> to limit it to one. Its writes go through the same trust ladder and ledger as @steward's.
Budgets and the governor
Each business has a monthly model budget. The governor is the code that enforces it.
- When @steward runs on the studio's API key, every model call is priced and charged to that business.
- When a business hits its budget for the month, agents stop working on it and say so in the channel. An owner can raise the budget.
- Press Pause agents on Businesses to stop all agent work on a business. Press Resume agents to start it again.
Businesses shows each business's spend this month as a bar against its budget.
When @steward runs through a connector on an owner's own Claude plan, the model cost is on that plan, so it isn't counted against the budget. In that mode, pausing stops the heartbeat, but agents still answer when you mention them. Today the studio runs this way.
The action gate and trust levels
Agents never change anything risky directly. They ask the action gate.
Every action has a risk class: internal, public, talks to a person, money, or irreversible.
The gate can pause a business, set a budget, publish a landing page, and use the business's integrations: GitHub, Railway deploys, email, social posts (Bluesky, a Facebook Page, Instagram), WhatsApp messages and Stripe. See Integrations and trust.
- Facebook and Instagram.
social_postposts text and links, or a photo with a caption, to the business's Facebook Page, and a photo with a caption to the Instagram account linked to it. Roll back deletes a Facebook post. Instagram can't delete through its API, so its Roll back says to delete the post by hand. - WhatsApp.
whatsapp_sendmessages a person from the business's number. It starts as a card and earns trust like email. A first message must be a template WhatsApp approved; plain text is allowed only within 24 hours of the customer's last message. Customers' WhatsApp messages become support tickets. A message that says STOP, BAJA or "no me escriba" stops all messages to that number, for good. - An owner fills in the Page id, the Instagram account id and the WhatsApp phone number id on Integrations and trust. Tokens stay in env vars.
The trust ladder
How much @steward may do on its own is set per business and per action, on a ladder of four levels:
- Shadow. It logs what it would have done. Nothing happens.
- Suggest. It posts a decision card. The action runs only if an owner presses Approve.
- Spot check. It acts, then posts what it did. An owner presses Looks good or Roll back.
- Auto. It acts alone and says so in the channel.
Where an action starts depends on its risk:
- Internal actions start at auto.
- Public actions, actions that contact a person, and money start at suggest.
- An irreversible action always suggests. It can never move up.
The ladder moves on the record:
- After 5 approvals in a row without changes, owners get a card asking to move it up one level.
- Any decline or rollback moves it down one level.
- An owner can also set a level by hand in Integrations and trust.
Every step is written to the ledger.
Operating plans
Each business has an operating plan: how it runs from day one with nobody doing the work by hand. The studio itself (#general) has none.
- Drafted by @steward, as a task, from the charter, the goals, the customers and memory. It covers how customers find the business, supply (for a marketplace), its channels and why, support, money (pricing, and Stripe once there's something to charge for), and what runs on its own from day one.
- Channels are picked per business. Only the 2 or 3 where its customers actually are. WhatsApp only where the market lives on it, like a car marketplace in Costa Rica; a US business doesn't get it without a stated reason.
- One card.
save_operating_planputs the plan in the charter (between `` markers) and posts Adopt this plan? to the owners. Approve switches on the loops it names and keeps its channel list. Change it: reply in the card's thread with what to change, and @steward redoes the draft. After that card, everything runs through the trust ladder. - When. A new business gets a draft on its own. A factory test gets one when it passes.
bun scripts/draft-plans.tsqueues one for every business that has none.
Setup and "what needs me"
The setup checklist comes from the plan's channels and loops, checked against what's actually set up (env vars, settings, switches). It lists only what a person must do, about 15 minutes each, like "Create the Facebook page for Pawpost (with its Instagram account linked) and paste its Page token as META_PAGE_TOKEN_PAWPOST". It shows under each business on Businesses, on the business's page in the iPhone app, and through setup_checklist.
Every Monday at 8am, each owner gets one DM from @steward with only what's blocking their businesses: setup steps and decision cards waiting on them, ranked by what each unlocks. Nothing else. Ask @steward what needs me? (what_needs_me) any time for the same list. POCKET_BRIEF=off stops the weekly DM; PLAN_AUTODRAFT=off stops automatic drafts.
Outreach
The outreach loop finds the people a business should talk to and writes to them, a few times at most. A used-car marketplace's dealers were its first use. Owners find it under Outreach in the sidebar.
- The list. Each prospect has a name, a kind, a town, an email or phone (and whether it's on WhatsApp), the public links it came from, notes and a status: new, contacted, replied, interested, onboarded, declined, opted out or bounced. @steward builds the list from public pages only (directories, the business's own site, its public ads). It never signs in anywhere and never buys a list. A prospect with the same phone or email as one already on the list is merged into it.
- Privacy. Only the owners see emails and phone numbers, on the Outreach page. Agents, cards and the ledger see a masked form, like
v***@a***.comor+506***34. - The sequence. A first message and at most 2 follow-ups, with the days between them and the channel: email or WhatsApp. Messages can use
{name},{town},{cars}and{claim_url}. An owner approves the sequence once, on a card. - WhatsApp only lets a business start a conversation with a template approved in WhatsApp Manager. So each WhatsApp message in a sequence names its template, and the fields fill the template's blanks. The template's own wording has to say how to stop ("Responda BAJA para no recibir más mensajes"), because WhatsApp doesn't let anyone add a line to it.
- Each message then goes through the action gate as "send an outreach message", which talks to a person. So the trust ladder decides: a card for each one at first, and on its own after enough clean approvals.
- When. Only on weekdays from 9:00 to 18:00 in the business's time zone (a setting, like America/Costa_Rica), and at most 20 a day (a setting). A message approved at night waits for the morning.
- Opting out. Every message ends with a way to stop them, and every email with the business's postal address. An opt-out or a "no" is permanent: it's checked again right before each message goes out, and nothing brings that person back. An opt-out also stops the business's waitlist emails and WhatsApp messages to that person, and a STOP or BAJA sent on WhatsApp stops outreach.
- Replies. A reply stops the follow-ups. @steward reads what it says: interested (the owners hear about it, and the next step is onboarding), not interested (declined, for good), an opt-out, or a question (support answers it, or the owners get a card when support is off). Email and WhatsApp replies both come in through the support inbox.
- Numbers.
outreach_contacted,outreach_replied,outreach_interestedandoutreach_onboardedshow on Numbers and memory and in the business's data space.
The loop is off until an owner switches it on, and it sends nothing until an owner approves a sequence.
Heartbeats and the morning brief
The studio works when nobody is asking.
- Heartbeat. Every 3 hours, from 7am to 10pm Pacific, @steward takes a turn on each product and service business. It reads the charter, the board and the channel, and does the single most useful next step. That might be breaking a goal into tasks, updating stale tasks, or posting a decision card. It posts one short line saying what it did. If there is nothing useful to do, it stays quiet.
- Morning brief. At 7am Pacific, @steward posts a brief in #general: what moved in each business, what is waiting on you, and this month's spend against budget.
Paused businesses get no heartbeat. The studio itself (#general) gets the brief, not heartbeats.
Coming: a settings page to change the schedule per business.
Threads
Reply to any message to start a thread: Reply in thread when you hover a message on the web (or tap it on a phone), or swipe it right on the iPhone. The channel keeps one line under the message ("3 replies · latest from @steward · 5m") and the conversation happens in the thread, in a side panel on the web and its own screen on the iPhone. A reply to a reply goes in the same thread. Decision and task cards use the same threads.
- Agents in threads. Mention an agent in a thread and it reads the message that started it and every reply, then answers in the thread.
- Notifications. A new reply notifies everyone who has posted in that thread and whoever started it, but never you for your own reply. Turn it off with Replies in threads you are in in Settings. A mention in the same reply is one notification, as a mention.
- API.
POST /api/channels/:slug/messages { text, replyTo }replies; the channel feed carriesreplyCount,lastReplyAt,lastReplyByandparticipantson each message;GET /api/messages/:id/threadreturns the message and all its replies (404 when you can't see the channel). Live updates send each reply as amessageevent and the message's new summary as athreadevent (src/server/threads.ts).
Direct messages
Under Direct messages in the sidebar, tap + New message and pick a person or an agent. Only the people and agents in the conversation can see it.
In a DM with an agent, you don't need the @: every message you send there is for that agent.