This is a complete A–Z breakdown of Kimi K3 what it is and how to run your entire business using only AI Agents
This will change everything about how you work with Kimi
TLDR;
If you don't want to read a 4480-word article, here is the GitHub repo which you can give to your agent.
https://github.com/codejunkie99/meridian-company-os Bookmark these builds before you forget.
Preface
The elements every company OS needs, derived from scratch, with the prompts that build each one. Learn the principle, take the prompt, build yours.
I built one (meridian-company-os, MIT licensed) and it is the reference for this guide, not the point of it. The point is the nine elements underneath it, because those are the elements underneath any company OS you will ever build.
Bookmark these 9 builds before you forget.

The Command cockpit: the operator surface from BUILD 5, and what the nine elements add up to.
Introduction
You are orchestrating agents with a chat window and a prayer. An agent that can spend money, ship code, and hire other agents is a company, and you are running that company with the tooling of a search box. This guide fixes that.
Here is the situation as of July 2026. A coding agent on the worker tier will generate any file you can describe in one paragraph, in under a minute, for cents.
It will also happily spend your budget, resolve its own approvals, and forget everything on refresh, because nobody built the walls. The model got cheap. The operating structure around it did not get built.
A company OS is not a nicer chat UI. It is the answer to six questions, at all times:
- who owns what
- which goals matter
- what is blocked
- how fast money is burning
- what is waiting on your yes
- what happened while you were gone
Answer all six and you have a company OS. Answer fewer and you have a demo.
This is not a philosophy of one, and it is not a walkthrough of my repo. It is the required elements, each with a generalized prompt you hand your coding agent, the reference implementation it produced for me, and a check that proves the element exists.
My stack was React 19 + TypeScript + Vite. Yours can be anything. The elements do not change.
How the whole thing was actually built, so you can copy the method and not just the output:
- Runtime: every file was generated by Kimi K3 through the Kimi Code CLI (~/.kimi-code/bin/kimi, kimi login once).
- Loop, nine times: write the prompt, pipe it through kimi -p, run the check. Wrong file? Fix the prompt and regenerate, never the file by hand.
- Tiering: K3 on the worker tier handled eight of nine builds; one (the reducer) got escalated to a frontier model.
- Skills, installed the whole run: Superpowers (obra/superpowers) for planning and review discipline; Context7 (upstash/context7) for version-accurate React 19 and Vite 6 docs, so K3 stopped hallucinating old APIs.
That is the entire toolchain. You will see it working inside every build below.
What you will have at the end, element by element:
- A vocabulary: the typed nouns every company OS must agree on (BUILD 0)
- A world: a seeded company mid-operation, so the console is never empty (BUILD 1)
- A single source of truth: one store, one reducer, no view owns state (BUILD 2)
- A heartbeat: the company moves on its own clock, not yours (BUILD 3)
- A memory: state that survives a reload (BUILD 4)
- An operator surface: one screen answering what happened, does it need me, what do I do (BUILD 5)
- A gate: money and power queue for your yes (BUILD 6)
- A command line: typed commands become real actions before any model is called (BUILD 7)
- A real runtime: an actual agent wired in, behind a fence (BUILD 8)
Who this is for: anyone with a terminal, a coding agent, and the intent to run more than one agent at a time. You do not copy my files. You take the principle and the prompt, and your agent writes your files.
How to read it: in order, doing the checks. Each element assumes the ones before it. The check is the proof the element exists; skip it and you are stacking on rumor.
The principles underneath everything. Three, and every design decision in the next nine builds is an instance of one of them:
- A company is state. Not vibes, not chat history. One tree of typed facts, and every screen is a window onto it, never a source of it.
- Power flows through gates. Anything that spends, hires, ships, or fires queues for an explicit yes. No gate, no company, just a leak with a UI.
- What is not written down did not happen. Every heartbeat, decision, and dollar lands in an append-only log. The log is the company's memory of itself.
No essays from here on.
Prerequisites
1node --version # v20+; any modern runtime works, this is what the reference used2npm --version # ships with node34# the coding agent that generates every file:5ls ~/.kimi-code/bin/kimi # kimi code cli installed6kimi login # once; stores oauth creds locally78# skills installed in kimi code before the first prompt:9# superpowers (github.com/obra/superpowers) planning + review discipline10# context7 (github.com/upstash/context7) fresh, version-accurate docs
Pin the runtime once: Kimi K3 is the worker tier that generated the reference files. Run a different model and you get different files, which is fine, because you are building yours, not mine. What you hold constant is the prompt and the check.
Why Kimi K3

Kimi K3 at a glance: launch specs and public benchmark standings, styled to match the console.
The runtime choice, fast. Not hype, just the tradeoffs.
What it is
- Moonshot AI's open-weight model, out July 16, 2026.
- 2.8T sparse MoE (16 of 896 experts per token), 1M context, native vision.
- First open 3T-class model, largest open-weight release to date.
- Full weights land July 27; hosted-only until then.
Where it loses (be honest)
- Fable 5 wins 22 of 35 on Moonshot's own launch table; K3 wins 12.
- 4th of 189 on the Intelligence Index (~57, vs Fable 5's 60 and GPT-5.6 Sol's 59).
- Sub-40% on FrontierMath Tier 4, where the closed frontier is near 90.
- Hallucination rate ~51%, so keep a verifier in the loop.
Where it wins (this exact workload)
- #1 on the blind Frontend Code Arena (1,679, ahead of Fable 5).
- 88.3% on Terminal-Bench 2.1; leads SWE Marathon and long-horizon agentic coding.
- The "survive a multi-step tool-call session without derailing" skill that makes or breaks an agent runtime.
Why it's the pick
- Open weights: self-host once they drop, own your runtime.
- Cheap: $0.30/M cache-hit input, $15/M output, ~70% under Fable 5.
- You run agents all day, and you can't rent your runtime, data, and cost curve from a vendor.
- Frontier-enough at agentic coding, open, and 70% cheaper beats rented benchmark points you can't run yourself.
The map
The nine elements, and the files they became in the reference implementation. Your file names will differ. Your elements will not.
1any-company-os/2 scaffold BUILD (this section): runtime + strict types, minimal deps3 domain model BUILD 0: the nouns -> src/lib/types.ts4 seeded world BUILD 1: never boot empty -> src/lib/seed.ts, skills.ts5 source of truth BUILD 2: one store -> src/lib/store.tsx6 heartbeat BUILD 3: the 2.6s tick -> src/lib/sim.ts7 memory BUILD 4: survive reload -> persistence in store.tsx8 operator surface BUILD 5: the cockpit -> src/App.tsx, views/Command.tsx9 the gate BUILD 6: approvals inbox -> views/Approvals.tsx10 command line BUILD 7: talk to it -> views/KimiSpace.tsx11 real runtime BUILD 8: fenced agent -> server/kimiBridge.ts, vite.config.ts
All ten prompts in this guide also ship as runnable files in prompts/, one per element, so kimi -p "$(cat prompts/00-scaffold.md)" works out of the box.
First, the scaffold. Principle: fewer dependencies, fewer lies. A company OS's one job is trustworthy state, and every dependency is someone else's state you now have to trust.
Prompt, through kimi -p:
1im building a company os. one console to run a whole company of humans2and ai agents from. pick me a lean setup: typed language, fast dev3loop, and as close to zero runtime deps as you can get away with. set4up the scaffold and tell me what you put in and why. anything you cant5justify in one line, rip out.
What K3 produced for the reference: React 19 + TypeScript 5.8 strict + Vite 6, and exactly one runtime dependency beyond React (lucide-react for icons). Scripts: dev, build (tsc -b && vite build), preview.
Context7 mattered here already: without it, K3 scaffolded React 18 patterns; with it, the config came out Vite 6 native on the first try.
CHECK: install and typecheck, exit code 0. Count your runtime dependencies; if you cannot justify each one in one line, this build is not done.
1npm install && npx tsc --noEmit; echo "exit: $?"
BUILD 0: The vocabulary
Why, in one line: a company is state, so before any behavior exists, every noun the company runs on must have exactly one typed definition.
First principles. Ask what irreducibly exists in any company of humans and agents, and you get seven nouns:
- an actor who works and costs money
- a goal that says why
- a task that says what, with a status and an owner
- a decision waiting on power (an approval)
- a unit of spend (the ledger)
- an event that says it happened (a log line)
- a container that holds them all (the company)
Every company OS is these seven nouns plus opinions. Type the nouns first and the opinions stay honest.
The generalized prompt. Notice it names the nouns and the two questions to ask of each, and nothing about my stack:
1ok before any logic i want the nouns. if im running a company of humans2and agents, what are the things that have to exist? actor, goal, task,3approval, money spent, a line in the log, the company holding it all.4model all of it in types, no behavior. for each noun ask two questions:5what does the operator need to see, and what does the system need to6enforce? statuses are closed unions not strings. money and tokens are7numbers not vibes. and if two of my nouns are secretly the same thing,8call it out, dont let me ship a mess.
Reference implementation. K3 emitted src/lib/types.ts, 416 lines, zero logic. The shapes that carry the whole system, and the enforcement question visible inside each one:
1export type AgentStatus = "working" | "idle" | "paused" | "blocked" | "offline";23export interface Agent {4 id: ID; companyId: ID; name: string; title: string; department: string;5 kind: "ai" | "human"; runtime?: string; model?: string; managerId?: ID;6 status: AgentStatus; heartbeat: string;7 monthlyBudget: number; spent: number; // enforce: spend has a ceiling8 successRate: number; tasksCompleted: number;9 skills: string[]; color: string; lastHeartbeat?: number;10}1112export type ApprovalType = "hire" | "spend" | "override" | "publish" | "terminate";13export interface Approval {14 id: ID; companyId: ID; type: ApprovalType; title: string; rationale: string;15 requestedBy: ID; amount?: number;16 status: "pending" | "approved" | "rejected"; // enforce: three states, no fourth17 checks: PolicyCheck[]; // the machine argues its case18 decidedAt?: number; decidedBy?: string;19}2021export interface ActivityEvent {22 id: ID; companyId: ID; ts: number; actorId: ID;23 kind: "heartbeat" | "task" | "delegation" | "spend"24 | "approval" | "governance" | "goal" | "system";25 message: string; amount?: number; // what is not written down did not happen26}
How the Kimi pass went: one kimi -p call, whole file in one shot.
The Superpowers skill earned its slot here. Its review discipline made K3 append a "two of your nouns overlap" note: a delegation and a task assignment were nearly the same noun, resolved as a delegatedBy field on Task instead of a new interface. That is the prompt's last sentence doing real work.
CHECK: typecheck passes with zero any. Then the noun audit: grep your interfaces and confirm each of the seven nouns has exactly one home.
1npx tsc --noEmit && grep -c "^export interface" src/lib/types.ts
BUILD 1: The world
Why, in one line: you cannot learn to operate an empty company, so the OS must boot into a world already mid-operation.
First principles. An operator surface teaches through recognition: you see an over-budget agent, a blocked task, a pending hire, and you learn what the controls do. An empty state teaches nothing, and it hides rendering bugs besides (an empty column and a broken column look identical).
So any company OS needs a deterministic seeded world: same world every boot, every status represented, every gate already holding a decision.
The generalized prompt:
1take the types we just wrote and fake me a whole company thats already2running, so the app has stuff on screen the second it boots. who works3here? give em real titles, who reports to who, budgets theyve half4burned through, hit rates. whats being worked on rn, whats stuck? what5decisions are sitting in my inbox waiting on a yes? every status in the6model shows up at least once. make it feel like i walked into a live7company at 2pm on a tuesday, not an empty template. no random(), same8world every boot.
Reference implementation. K3 produced two files.
seed.ts (~600 lines of fixtures):
- two companies, six-plus agents each with reporting lines and part-spent budgets
- a mission-to-company-to-team goal tree
- tasks across all six statuses
- pending approvals with policy-check results
And skills.ts, a registry of installable capability packs credited to their real sources:
1export const SKILLS: Skill[] = [2 s("superpowers", "Superpowers", "obra/superpowers",3 "https://github.com/obra/superpowers", "developer",4 "Battle-tested workflow superpowers: TDD, debugging, planning, and review discipline."),5 s("context7", "Context7", "upstash/context7",6 "https://github.com/upstash/context7", "developer",7 "Pulls fresh, version-accurate library docs into the agent's context window."),8 // ...design, marketing, social, finance, operations, legal packs9];
There is a loop worth noticing. The two skills running inside my Kimi Code CLI while it generated this file are the first two entries in the registry the file defines.
The OS you are building installs skills on its agents the same way you just installed skills on the agent building it. The method is the product.
One regeneration was needed here. K3's first pass put every task in in_progress, which fails the "every status at least once" line. The fix was not editing the seed; it was that line getting added to the prompt, then kimi -p again. Fix the prompt, never the file.
CHECK: boot twice, diff the world. Deterministic means identical.
1node -e "const {seedState}=await import('./src/lib/seed.ts'); \2 console.log(Object.keys(seedState().tasks).length)" # same count, every run
BUILD 2: The source of truth
Why, in one line: a company is state, so state may only change in one place, and everything else is a window.
First principles. The failure mode of every multi-agent dashboard is the same: five components each holding a copy of the truth, drifting. The cure is structural, not disciplinary.
One store. One reducer, a pure function from state plus action to state. Views read; views dispatch; views never mutate.
Do this and every later element (the log, the gate, the heartbeat) becomes a reducer case instead of an architecture decision.
The generalized prompt:
1every screen should just be a window onto one source of truth, not its2own little pile of state everywhere. build me that source. if the store3is the ONLY place state can change, what shape is it, and how does a4button ask it to change something without reaching in and mutating junk?5one pure reducer, one action union, selectors for the common reads.6keep it so i can bolt on new actions later without rewriting the world.7build it. then tell me the one case youd bet money i break first.
Reference implementation. src/lib/store.tsx, about 1,400 lines: StoreProvider, useStore() returning { state, dispatch }, a reducer covering every member of the Action union, and selectors (companyAgents, companyTasks, companyApprovals, agentName). The case that carries principle two and three at once:
1case "decideApproval": {2 const a = s.approvals[action.id];3 if (!a || a.status !== "pending") return s; // no double-deciding4 const decided = { ...a, status: action.approve ? "approved" : "rejected",5 decidedAt: Date.now(), decidedBy: "you" } as const;6 return {7 ...s,8 approvals: { ...s.approvals, [a.id]: decided },9 activity: [{ id: crypto.randomUUID(), companyId: a.companyId,10 ts: Date.now(), actorId: "you", kind: "governance",11 message: `${action.approve ? "Approved" : "Rejected"}: ${a.title}` },12 ...s.activity], // the decision is written down13 };14}
How the Kimi pass went: this is the one build K3 stalled on. Two worker-tier attempts produced reducers that mutated nested objects in three cases, both caught by the check, not by reading the diff. The build got escalated to a frontier model, same prompt verbatim, clean on the first pass.
That is the tiering rule in practice: K3 is the default worker, the frontier model is reserved for the one build where purity is the whole point. Asked for the case it would bet money I break first, it named deciding an already-decided approval, hence the status !== "pending" guard.
CHECK: exhaustiveness by grep. Every action in the union has a case, or it is a dead button waiting to be pressed.
1grep -c 'case "' src/lib/store.tsx # >= the number of members in your Action union2npx tsc --noEmit
BUILD 3: The heartbeat
Why, in one line: real companies move while you are not looking, so the OS must tick on its own clock or it is training you on a lie.
First principles. Agents spend and progress continuously; your attention is discrete. A console that only changes when you click teaches you that nothing happens between clicks, which is exactly wrong and exactly expensive.
So any company OS needs a heartbeat: a small, bounded, pure state transition fired on a timer. Bounded is the load-bearing word. An unbounded tick is a runaway; a bounded one costs pennies of simulated spend and proves the pipes.
The generalized prompt:
1i want this thing alive even with zero real agents plugged in, so when2i open it the numbers are already moving. picture one heartbeat of a3running company, like 2 seconds of it. what changes? a bit of money4burns, some work creeps forward, a log line drops. make that one step a5pure state -> state function so i can fire it on a timer and trust it6never corrupts anything. cap everything: spend per tick, progress per7tick, log length. whats the smallest believable amount of movement, and8how do i stop it running away? build the tick and defend the numbers9you picked.
Reference implementation. src/lib/sim.ts: runTick(state): State, fired every 2,600 ms by a single interval in the provider. Per tick: one working agent accrues $0.40 to $3.04, one open task gains 3 to 10 percent progress, a heartbeat line lands, and the log is capped at 500 entries newest-first.
Asked to defend the numbers, K3's answer survives as the design: a tick just visible to a human eye (2.6s), spend small enough that an hour of simulation costs pocket change, a log cap so memory cannot creep.
1export const TICK_MS = 2600;23export function runTick(s: State): State {4 const tickN = Math.floor(Date.now() / 1000);5 const agents = Object.values(s.agents).filter((a) => a.status === "working");6 if (agents.length === 0) return s;7 const actor = pick(agents, tickN);8 const spend = +(0.4 + (tickN % 13) * 0.22).toFixed(2); // $0.40..$3.04, bounded9 // ...advance one task, append events...10 return { ...s, activity: [...events, ...s.activity].slice(0, 500) }; // capped11}
And exactly one interval, wired by a follow-up kimi -p edit scoped to a single hook: while simRunning, dispatch {type:"tick"} every TICK_MS, clear on cleanup, intervals nowhere else.
CHECK: watch the feed for 30 seconds; lines land roughly every 2.6s. Pause; it freezes. Resume; it moves. Lines arriving faster than 2.6s means two intervals, and two intervals means some component is doing the provider's job.
BUILD 4: The memory
Why, in one line: a company that forgets itself on refresh is a demo, and the line between demo and system is a reload.
First principles. Split all state into two kinds and the design writes itself. Domain state (who works here, what was spent, what was decided) is the company; it must survive. Session state (which screen you were on, an open modal, a toast) is your visit; persisting it is a bug.
So: an allowlist snapshot of domain slices, written on a debounce after real edits, restored on boot. And one ordering law: never write before the restore completes, or you overwrite the company with a blank.
The generalized prompt:
1rn a refresh nukes the whole company back to seed. thats a demo not a2system. i want state to survive reload. think about what actually3deserves to be saved vs what doesnt: whats real company data vs whats4just where i happened to be clicking? allowlist the real stuff,5explicitly exclude the session junk. whats the fail case if i save at6the wrong second, and how do i make sure i never overwrite good data7with a half-loaded blank? build the save + restore path and warn me8about the ordering trap before i step in it.
Reference implementation. A hydrate action, a persistable() allowlist (companies, agents, goals, tasks, approvals, runs, activity, customSkills, activeCompanyId), a 400 ms debounced write, and the trap named in the prompt guarded by one line:
1useEffect(() => {2 if (!state.hydrated) return; // the ordering law: never write before restore3 const id = window.setTimeout(() => {4 window.localStorage.setItem(SNAPSHOT_KEY, JSON.stringify(persistable(state)));5 }, 400);6 return () => window.clearTimeout(id);7}, [state]);
How the Kimi pass went: a kimi -p edit against the existing store, not a fresh file. The prompt's warning clause is why the hydrated gate exists.
Asked to name the trap before stepping in it, K3 named this exact race: first render fires the persist effect before the snapshot loads, writing seed over saved data. Cheap models find real bugs when the prompt makes finding bugs the deliverable.
CHECK: two states, both reachable. Let the sim burn for 20 seconds, refresh, numbers continue. Clear the key, refresh, clean seed returns.
1# in the browser console:2localStorage.removeItem("meridian.snapshot") # yours will differ; then reload
BUILD 5: The operator surface
Why, in one line: the operator asks three questions in a fixed order (what is happening, does it need me, what do I do), and the main screen must answer them top to bottom.
First principles. Derive the cockpit from the questions, not from what looks good in a screenshot:
- What is happening: one north-star number plus a live feed.
- Does it need me: a risk radar (who is blocked, who is over budget) and a count of decisions waiting.
- What do I do: every one of those is a click deep, not a hunt.
And because a company is state, every widget is a pure read of the store. A cockpit that caches its own numbers is an instrument that lies.
The generalized prompt:
1i need the one screen i actually stare at all day. im the operator.2when i open it it answers, in this order: whats happening, does it need3me, what do i do about it. so: the one number that says are we winning,4whos on fire or over budget, whats waiting on my yes, how fast money is5burning per team, and a live feed of what just happened. every widget6just reads the store, nothing owns its own state, everything that needs7me is one click from here. build the shell + this cockpit, top to8bottom in that order.
Reference implementation. A sidebar shell (nav, company switcher, sim toggle) and CommandView: north-star with delta, risk radar, pending-approvals count that deep-links to the gate, department burn bars from company.budgets, newest-20 activity feed. All selectors, no local intervals, machine values in mono.
Context7 earned its keep again: React 19 memoization guidance came in current, so per-tick re-renders stay cheap without stale-API workarounds.

The cockpit is one operator surface; the Work board is another, built from the same store and the same tokens. Once BUILD 2 exists, every surface is a pure window onto it.
CHECK: open the cockpit cold and answer the three questions aloud in under ten seconds without clicking. Switch companies; every widget swaps with no stale number bleeding through. Click the approvals count; you land on the gate.
BUILD 6: The gate
Why, in one line: power flows through gates, so nothing that spends, hires, ships, or fires resolves without an explicit recorded yes.
First principles. Autonomy is a budget, not a right. The five verbs that can hurt you (spend, hire, override, publish, terminate) each need the same four things at decision time:
- the ask
- the asker's rationale
- the machine's own policy checks, argued in the open
- a decision that lands in the permanent log with a name and a timestamp
One more, easy to miss: when a policy check fails, approving must feel like overriding. Defaults are where governance goes to die.
The generalized prompt:
1heres the rule for the whole thing: an agent never spends money, hires,2ships something public, or fires anyone without me saying yes. build me3the one inbox where that yes lives. every item shows the ask, whos4asking, why, how much, and the systems own policy checks (in budget? is5there a manager? under the cap?) so i see its reasoning before i decide.6approve and reject both leave a permanent trace in the log with my name7on it, not just a ui toggle. and if a check failed, dont make approve8the easy default, make me override on purpose. build the inbox.

The gate. Every card shows the ask, the requester, the rationale, the amount, and the system's own policy checks. Approve and reject are the only exits, and both write to the log.
Reference implementation. ApprovalsView: pending first, each card with type badge, requester, rationale, amount in mono, and pass/fail policy checks with detail text. The default-flipping line, exactly as the prompt demanded:
1const failed = a.checks.some((c) => !c.passed);2// ...3<button className={failed ? "danger" : "primary"}4 onClick={() => dispatch({ type: "decideApproval", id: a.id, approve: true })}>5 {failed ? "Override and approve" : "Approve"}6</button>
The decision itself is BUILD 2's decideApproval case, which is the point: the gate is a screen, but the law lives in the reducer. A gate enforced in the UI is a suggestion.
CHECK: approve one seeded item; it moves to decided, stamped "approved by you," and a governance line lands in the feed. Find an item with a failing check; the button reads "Override and approve."
Then watch the sim for a minute: if any approval resolves without your click, the gate is broken and nothing else matters until it is fixed.
BUILD 7: The command line
Why, in one line: operators issue orders, and an order that costs a model call to parse is slower, pricier, and less deterministic than a regex.
First principles. There are two kinds of operator utterances. Commands ("create task x, assign to bea, p1", "move MER-1042 to review", "budget report") have fixed grammar and a known action: parse them locally, dispatch the real store action, print exactly what changed. Total cost zero, latency zero.
Everything else is conversation, and that is what the model is for. The routing rule is deterministic first, model as fallback, and always show the trace. An OS that does things silently is indistinguishable from one that does nothing.
The generalized prompt:
1fastest way to run this company is a command line, not clicking around.2i want a chat where i type "create task: fix onboarding, assign to bea,3p1" or "move MER-1042 to review" or "budget report" and it just DOES4it, hits the store, shows me exactly what changed. no model call, no5cost, no waiting, when its a known command. only when nothing matches6does it fall through to an actual model later. parse first, dispatch7the real action, print the trace. and dont route to a model for8anything you can just execute.

Typed commands hit the store directly and print the trace, no model call. Only a non-command sentence falls through to the local K3 runtime, shown here answering with its `kimi -p (local, k3)` trace.
Reference implementation.
KimiSpaceView: regex parse against the command grammar, name-to-id resolution through the store's selectors, dispatch, trace line back into the chat. The fallback branch prints a placeholder this build, because the model is BUILD 8's problem:
1if ((m = input.match(/^create task:\s*(.+?),\s*assign to\s+(\w+),\s*(p[0-3])$/i))) {2 dispatch({ type: "createTask", title: m[1],3 assigneeId: agentIdByName(m[2]), priority: m[3] as never, by: "you" });4 next.push({ role: "system", body: `Created task "${m[1]}" (${m[3]}) -> ${m[2]}` });5} else {6 next.push({ role: "assistant", body: "(would route to local kimi runtime)" });7}
CHECK: create task: refresh onboarding emails, assign to Bea, p1 creates a real task with a generated code and prints the trace. budget report prints spend against limit per department, instantly, no spinner, because no model was called. A nonsense sentence hits the placeholder.
Command handling that shows a loading state is a model call you are paying for and should not be.
BUILD 8: The real runtime
Why, in three lines: everything so far runs on a simulation, and a company OS that never touches a real agent is a diorama. The last element is the bridge to an actual runtime, and it is the most dangerous file in the system, because it spawns a process that can think and spend.
So the fence is the feature: concurrency of one, an input cap, a kill timer, an isolated workdir, and credentials that never cross back over the wire.
First principles. Whatever your runtime (a CLI, an API, a queue), the bridge needs the same five walls, each answering one attack:
- How many at once: one, or a stuck run becomes a stampede.
- How big an input: capped, or someone pastes a book into your budget.
- How long: a kill timer, or one hang holds the lock forever.
- Where: a dedicated workdir, or the agent reads your repo.
- What leaks back: nothing. No token or credential in any response, ever.
And one grace rule: if the runtime is absent, degrade to simulation, never crash. The console must outlive its agents.
The generalized prompt:
1ok everything so far is fake, a nice sim. now wire in my real agent. i2have a real cli installed and logged in on this machine. when i type3something that is NOT a known command, spawn the real agent and answer4with the actual model, my own creds. but this is the scariest path in5the app, its spawning a process that can spend, so fence it hard and6tell me the fence before you build it: how many run at once, how big an7input, how long before you kill it, where it runs, what must never leak8back out. and if the cli isnt there, dont crash, stay in sim mode.
Reference implementation. Kimi is both the builder and the built here: the runtime the bridge spawns is the same ~/.kimi-code/bin/kimi that generated every file above, invoked as kimi -p with the operator's message on stdin, reusing the kimi login credentials.
The fence, as shipped:
- one chat at a time (a second concurrent request gets HTTP 409)
- 8,000-character input cap
- 180-second kill timer
- a dedicated .kimi-runtime workdir
- no endpoint that returns a token
Two endpoints: GET /local-runtime/status and POST /local-runtime/kimi/chat. And because the bridge spawns a local process, the dev server binds to 127.0.0.1 only:
1export default defineConfig({2 plugins: [react(), kimiOAuthProxy(), localKimiBridge()],3 // local-first: the bridge spawns your cli, never expose beyond this machine4 server: { port: 4173, host: "127.0.0.1" },5 preview: { port: 4173, host: "127.0.0.1" },6});
A final kimi -p edit swapped BUILD 7's placeholder branch for the real POST, with the offline fallback intact.
CHECK: prove the fence, not the feature. Status endpoint reports the CLI; a non-command sentence gets answered by the real model. Then attack it: two chats at once, the second returns 409; paste 9,000 characters, rejected before spawn; rename the CLI binary, the app stays up in sim mode.
1curl -s http://127.0.0.1:4173/local-runtime/status
The rollout
An OS you built in a weekend still gets adopted in weeks. Graduate.
- Week 1, Observe. Builds 0 to 5, simulation only. Watch the tick move money and work. Graduate when you answer the cockpit's three questions in under ten seconds.
- Week 2, Gate. Builds 6 and 7. Decide seeded approvals; run the company by typed commands. Graduate when every decision you make shows a matching governance line in the log.
- Week 3, Connect. Build 8. Real runtime answers chat, autopilot stays off. Graduate when the 409, the 8k cap, and the kill timer all fire when you attack them.
- Week 4, Operate. One real agent, one real task, one small budget. Review every run. Graduate when a full day passes with zero spend you did not expect and zero decisions you did not see.

Week 4 is where the numbers stop being simulated. Department burn, projections, and a model/token ledger, every real dollar traceable to an agent and a task.
Each row unlocks the next. Week 4 on day 1 is how you fund a educational invoice.
The runbook
Every alarm the system raises, and the move. The signal is generic to any company OS; the action is where the reference points you.
- Feed frozen, sim on. Duplicate or missing interval, or a view mutated state. One setInterval, in the provider, only. Views are windows.
- Numbers reset on refresh. Snapshot wrote before hydrate, or never wrote. Check the hydrated gate on the persist effect.
- Approval resolved itself. Something besides the decide action flips status. Only decideApproval may touch approval status; audit the reducer.
- Burn bar over 100%. A department passed its limit. A spend gate should already be pending; if not, the check is missing.
- Command does nothing. Grammar missed or a name did not resolve. Echo the parse; confirm the name exists in the store.
- Chat returns 409. A real run is already in flight. Wait. The one-at-a-time wall is working.
- Chat returns 502. The auth proxy could not reach upstream. Network problem; the app must still run in sim mode.
- Status says CLI missing. Runtime absent or not logged in. Run kimi login, or stay in simulation. Never crash.
- Typecheck fails after a generation. The generated file drifted from the model. Fix the prompt, regenerate. Hand-patched files are unreproducible builds.
The Rules (print this)
- A company is state: one typed tree, and every screen is a window, never a source.
- Seven nouns, one definition each. Two nouns that overlap is a mess you will ship.
- Never boot empty. Every status in the model appears in the seed at least once.
- State changes in one reducer or it does not change. A button that does not dispatch is dead.
- The heartbeat is bounded: capped spend, capped progress, log capped at 500.
- Domain state persists; session state never does. Never write before hydrate.
- Power flows through gates: spend, hire, override, publish, terminate all queue for a yes.
- A failed policy check makes approval an explicit override, never the default.
- The gate's law lives in the reducer. A gate enforced in the UI is a suggestion.
- What is not written down did not happen. Every decision logs a name and a timestamp.
- Deterministic first, model as fallback. Never pay a model to parse a regex.
- Five walls on any real runtime: one at a time, capped input, kill timer, isolated workdir, zero credential leakage.
- The runtime dies, the console lives. Absent CLI means sim mode, never a crash.
- Bind local, 127.0.0.1. A bridge that spawns your CLI is not a thing you expose.
- Fix the prompt, never the file. K3 is the worker; escalate one build, not the project.
Closing
The repo was never the point. Meridian is one implementation, in one stack, of nine elements that do not care about your stack: vocabulary, world, truth, heartbeat, memory, surface, gate, command line, runtime.
Build those nine in Rails or Rust or a spreadsheet with macros and you have a company OS. Skip the gate or the log and you have a leak with a UI, in any language.
And notice what actually built it: a worker-tier model, two skills, nine prompts, and a check after each. The method you just read is the machine it produces. You write the spec, a cheap agent writes the files, the walls keep everyone honest, including you.
Tonight: write BUILD 0. Open your agent, hand it the vocabulary prompt, and make it name the seven nouns of your company. Do not let it write a single behavior. The nouns are the whole first night, and everything else is a window onto them.
So here is the question worth arguing about: what are the seven nouns of your company, and which two did you almost merge? Build BUILD 0 tonight and reply with your types.
Built from my own notes while constructing the reference repo; every file was generated by Kimi K3 through the Kimi Code CLI, and a frontier model edited this prose and handled one escalated build (the reducer). The claims are checkable against (https://github.com/codejunkie99/meridian-company-os).
This is written by the authors' notes while building with Kimi K3 and Kimi Code CLI and it has been edited by Kimi K3 Code and Opus 4.7.





