# How to Build a Company OS using Kimi K3 (Builder's Guide)
**作者**: Avid
**日期**: 2026-07-21T16:41:39.000Z
**来源**: [https://x.com/Av1dlive/status/2079607748396994789](https://x.com/Av1dlive/status/2079607748396994789)
---

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:
1. 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.
2. 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.
3. 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
```
node --version # v20+; any modern runtime works, this is what the reference used
npm --version # ships with node
# the coding agent that generates every file:
ls ~/.kimi-code/bin/kimi # kimi code cli installed
kimi login # once; stores oauth creds locally
# skills installed in kimi code before the first prompt:
# superpowers (github.com/obra/superpowers) planning + review discipline
# 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.
```
any-company-os/
scaffold BUILD (this section): runtime + strict types, minimal deps
domain model BUILD 0: the nouns -> src/lib/types.ts
seeded world BUILD 1: never boot empty -> src/lib/seed.ts, skills.ts
source of truth BUILD 2: one store -> src/lib/store.tsx
heartbeat BUILD 3: the 2.6s tick -> src/lib/sim.ts
memory BUILD 4: survive reload -> persistence in store.tsx
operator surface BUILD 5: the cockpit -> src/App.tsx, views/Command.tsx
the gate BUILD 6: approvals inbox -> views/Approvals.tsx
command line BUILD 7: talk to it -> views/KimiSpace.tsx
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:
```
im building a company os. one console to run a whole company of humans
and ai agents from. pick me a lean setup: typed language, fast dev
loop, and as close to zero runtime deps as you can get away with. set
up the scaffold and tell me what you put in and why. anything you cant
justify 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.
```
npm 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:
```
ok before any logic i want the nouns. if im running a company of humans
and agents, what are the things that have to exist? actor, goal, task,
approval, money spent, a line in the log, the company holding it all.
model all of it in types, no behavior. for each noun ask two questions:
what does the operator need to see, and what does the system need to
enforce? statuses are closed unions not strings. money and tokens are
numbers not vibes. and if two of my nouns are secretly the same thing,
call 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:
```
export type AgentStatus = "working" | "idle" | "paused" | "blocked" | "offline";
export interface Agent {
id: ID; companyId: ID; name: string; title: string; department: string;
kind: "ai" | "human"; runtime?: string; model?: string; managerId?: ID;
status: AgentStatus; heartbeat: string;
monthlyBudget: number; spent: number; // enforce: spend has a ceiling
successRate: number; tasksCompleted: number;
skills: string[]; color: string; lastHeartbeat?: number;
}
export type ApprovalType = "hire" | "spend" | "override" | "publish" | "terminate";
export interface Approval {
id: ID; companyId: ID; type: ApprovalType; title: string; rationale: string;
requestedBy: ID; amount?: number;
status: "pending" | "approved" | "rejected"; // enforce: three states, no fourth
checks: PolicyCheck[]; // the machine argues its case
decidedAt?: number; decidedBy?: string;
}
export interface ActivityEvent {
id: ID; companyId: ID; ts: number; actorId: ID;
kind: "heartbeat" | "task" | "delegation" | "spend"
| "approval" | "governance" | "goal" | "system";
message: string; amount?: number; // what is not written down did not happen
}
```
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.
```
npx 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:
```
take the types we just wrote and fake me a whole company thats already
running, so the app has stuff on screen the second it boots. who works
here? give em real titles, who reports to who, budgets theyve half
burned through, hit rates. whats being worked on rn, whats stuck? what
decisions are sitting in my inbox waiting on a yes? every status in the
model shows up at least once. make it feel like i walked into a live
company at 2pm on a tuesday, not an empty template. no random(), same
world 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:
```
export const SKILLS: Skill[] = [
s("superpowers", "Superpowers", "obra/superpowers",
"https://github.com/obra/superpowers", "developer",
"Battle-tested workflow superpowers: TDD, debugging, planning, and review discipline."),
s("context7", "Context7", "upstash/context7",
"https://github.com/upstash/context7", "developer",
"Pulls fresh, version-accurate library docs into the agent's context window."),
// ...design, marketing, social, finance, operations, legal packs
];
```
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.
```
node -e "const {seedState}=await import('./src/lib/seed.ts'); \
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:
```
every screen should just be a window onto one source of truth, not its
own little pile of state everywhere. build me that source. if the store
is the ONLY place state can change, what shape is it, and how does a
button ask it to change something without reaching in and mutating junk?
one pure reducer, one action union, selectors for the common reads.
keep it so i can bolt on new actions later without rewriting the world.
build 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:
```
case "decideApproval": {
const a = s.approvals[action.id];
if (!a || a.status !== "pending") return s; // no double-deciding
const decided = { ...a, status: action.approve ? "approved" : "rejected",
decidedAt: Date.now(), decidedBy: "you" } as const;
return {
...s,
approvals: { ...s.approvals, [a.id]: decided },
activity: [{ id: crypto.randomUUID(), companyId: a.companyId,
ts: Date.now(), actorId: "you", kind: "governance",
message: `${action.approve ? "Approved" : "Rejected"}: ${a.title}` },
...s.activity], // the decision is written down
};
}
```
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.
```
grep -c 'case "' src/lib/store.tsx # >= the number of members in your Action union
npx 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:
```
i want this thing alive even with zero real agents plugged in, so when
i open it the numbers are already moving. picture one heartbeat of a
running company, like 2 seconds of it. what changes? a bit of money
burns, some work creeps forward, a log line drops. make that one step a
pure state -> state function so i can fire it on a timer and trust it
never corrupts anything. cap everything: spend per tick, progress per
tick, log length. whats the smallest believable amount of movement, and
how do i stop it running away? build the tick and defend the numbers
you 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.
```
export const TICK_MS = 2600;
export function runTick(s: State): State {
const tickN = Math.floor(Date.now() / 1000);
const agents = Object.values(s.agents).filter((a) => a.status === "working");
if (agents.length === 0) return s;
const actor = pick(agents, tickN);
const spend = +(0.4 + (tickN % 13) * 0.22).toFixed(2); // $0.40..$3.04, bounded
// ...advance one task, append events...
return { ...s, activity: [...events, ...s.activity].slice(0, 500) }; // capped
}
```
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:
```
rn a refresh nukes the whole company back to seed. thats a demo not a
system. i want state to survive reload. think about what actually
deserves to be saved vs what doesnt: whats real company data vs whats
just where i happened to be clicking? allowlist the real stuff,
explicitly exclude the session junk. whats the fail case if i save at
the wrong second, and how do i make sure i never overwrite good data
with a half-loaded blank? build the save + restore path and warn me
about 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:
```
useEffect(() => {
if (!state.hydrated) return; // the ordering law: never write before restore
const id = window.setTimeout(() => {
window.localStorage.setItem(SNAPSHOT_KEY, JSON.stringify(persistable(state)));
}, 400);
return () => window.clearTimeout(id);
}, [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.
```
# in the browser console:
localStorage.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:
```
i need the one screen i actually stare at all day. im the operator.
when i open it it answers, in this order: whats happening, does it need
me, what do i do about it. so: the one number that says are we winning,
whos on fire or over budget, whats waiting on my yes, how fast money is
burning per team, and a live feed of what just happened. every widget
just reads the store, nothing owns its own state, everything that needs
me is one click from here. build the shell + this cockpit, top to
bottom 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:
```
heres the rule for the whole thing: an agent never spends money, hires,
ships something public, or fires anyone without me saying yes. build me
the one inbox where that yes lives. every item shows the ask, whos
asking, why, how much, and the systems own policy checks (in budget? is
there a manager? under the cap?) so i see its reasoning before i decide.
approve and reject both leave a permanent trace in the log with my name
on it, not just a ui toggle. and if a check failed, dont make approve
the 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:
```
const failed = a.checks.some((c) => !c.passed);
// ...
<button className={failed ? "danger" : "primary"}
onClick={() => dispatch({ type: "decideApproval", id: a.id, approve: true })}>
{failed ? "Override and approve" : "Approve"}
</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:
```
fastest way to run this company is a command line, not clicking around.
i want a chat where i type "create task: fix onboarding, assign to bea,
p1" or "move MER-1042 to review" or "budget report" and it just DOES
it, hits the store, shows me exactly what changed. no model call, no
cost, no waiting, when its a known command. only when nothing matches
does it fall through to an actual model later. parse first, dispatch
the real action, print the trace. and dont route to a model for
anything 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:
```
if ((m = input.match(/^create task:\s*(.+?),\s*assign to\s+(\w+),\s*(p[0-3])$/i))) {
dispatch({ type: "createTask", title: m[1],
assigneeId: agentIdByName(m[2]), priority: m[3] as never, by: "you" });
next.push({ role: "system", body: `Created task "${m[1]}" (${m[3]}) -> ${m[2]}` });
} else {
next.push({ role: "assistant", body: "(would route to local kimi runtime)" });
}
```
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:
```
ok everything so far is fake, a nice sim. now wire in my real agent. i
have a real cli installed and logged in on this machine. when i type
something that is NOT a known command, spawn the real agent and answer
with the actual model, my own creds. but this is the scariest path in
the app, its spawning a process that can spend, so fence it hard and
tell me the fence before you build it: how many run at once, how big an
input, how long before you kill it, where it runs, what must never leak
back 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:
```
export default defineConfig({
plugins: [react(), kimiOAuthProxy(), localKimiBridge()],
// local-first: the bridge spawns your cli, never expose beyond this machine
server: { port: 4173, host: "127.0.0.1" },
preview: { port: 4173, host: "127.0.0.1" },
});
```
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.
```
curl -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)
1. A company is state: one typed tree, and every screen is a window, never a source.
2. Seven nouns, one definition each. Two nouns that overlap is a mess you will ship.
3. Never boot empty. Every status in the model appears in the seed at least once.
4. State changes in one reducer or it does not change. A button that does not dispatch is dead.
5. The heartbeat is bounded: capped spend, capped progress, log capped at 500.
6. Domain state persists; session state never does. Never write before hydrate.
7. Power flows through gates: spend, hire, override, publish, terminate all queue for a yes.
8. A failed policy check makes approval an explicit override, never the default.
9. The gate's law lives in the reducer. A gate enforced in the UI is a suggestion.
10. What is not written down did not happen. Every decision logs a name and a timestamp.
11. Deterministic first, model as fallback. Never pay a model to parse a regex.
12. Five walls on any real runtime: one at a time, capped input, kill timer, isolated workdir, zero credential leakage.
13. The runtime dies, the console lives. Absent CLI means sim mode, never a crash.
14. Bind local, 127.0.0.1. A bridge that spawns your CLI is not a thing you expose.
15. 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.
## 相关链接
- [Avid](https://x.com/Av1dlive)
- [@Av1dlive](https://x.com/Av1dlive)
- [26K](https://x.com/Av1dlive/status/2079607748396994789/analytics)
- [https://github.com/codejunkie99/meridian-company-os](https://github.com/codejunkie99/meridian-company-os)
- [meridian-company-os](https://github.com/codejunkie99/meridian-company-os)
- [https://github.com/codejunkie99/meridian-company-os](https://github.com/codejunkie99/meridian-company-os)
- [Upgrade to Premium](https://x.com/i/premium_sign_up)
- [12:41 AM · Jul 22, 2026](https://x.com/Av1dlive/status/2079607748396994789)
- [26K Views](https://x.com/Av1dlive/status/2079607748396994789/analytics)
- [View quotes](https://x.com/Av1dlive/status/2079607748396994789/quotes)
---
*导出时间: 2026/7/22 09:34:45*
---
## 中文翻译
# 如何使用 Kimi K3 构建公司操作系统(构建者指南)
**作者**: Avid
**日期**: 2026-07-21T16:41:39.000Z
**来源**: [https://x.com/Av1dlive/status/2079607748396994789](https://x.com/Av1dlive/status/2079607748396994789)
---

这是关于 Kimi K3 的完整 A–Z 解析,涵盖它是什么以及如何仅使用 AI 智能体来运营你的整个业务。
这将改变你与 Kimi 协作的一切方式。
> TLDR; 如果你不想阅读这篇 4480 字的文章,这里有一个 GitHub 仓库,你可以直接交给你的智能体。https://github.com/codejunkie99/meridian-company-os
> 趁你还没忘,把这些构建收藏起来。
## 前言
每个公司操作系统所需的要素,从零开始推导,并附有构建每个要素的提示词。学习原理,获取提示词,构建你自己的系统。
我已经构建了一个(meridian-company-os,MIT 许可),它是本指南的参考,但不是本指南的重点。重点是底层的九个要素,因为它们是你将要构建的任何公司操作系统的底层要素。
趁你还没忘,收藏这 9 个构建。

指挥驾驶舱:来自构建 5 的操作员界面,以及九个要素的总和。
# 简介
你正用一个聊天窗口和祈祷来编排智能体。一个能花钱、发布代码和雇佣其他智能体的智能体就是一个公司,而你正在用搜索框级别的工具来运营这个公司。本指南将解决这个问题。
截至 2026 年 7 月的情况是这样的。一个工作层的编码智能体将在一分钟内,以几分钱的成本,生成任何你能用一段话描述的文件。
它也会乐于花光你的预算,自行解决审批,并在刷新后忘掉一切,因为没有人构建好围墙。模型变便宜了,但围绕它的操作结构并没有建立起来。
公司操作系统不是一个更好看的聊天 UI。它是对六个问题的实时回答:
- 谁拥有什么
- 哪些目标重要
- 什么被阻塞了
- 资金消耗有多快
- 什么在等待你的同意
- 你不在期间发生了什么
回答了全部六个,你就拥有了一个公司操作系统。回答得更少,你拥有的只是一个演示。
这不是一家之言,也不是我的代码库的演练。这是必需的要素,每一个都附带一个通用的提示词(你可以交给你的编码智能体)、它为我生成的参考实现,以及一个证明该要素存在的检查。
我的技术栈是 React 19 + TypeScript + Vite。你的可以是任何东西。这些要素不会改变。
整个过程实际上是如何构建的,这样你可以复制方法而不仅仅是复制输出:
- Runtime:每个文件都是由 Kimi K3 通过 Kimi Code CLI 生成的(~/.kimi-code/bin/kimi,kimi login 一次)。
- 循环九次:编写提示词,通过 kimi -p 传输,运行检查。文件错了?修复提示词并重新生成,永远不要手动修改文件。
- 分层:K3 在工作层处理了九个构建中的八个;一个(reducer)被升级到了前沿模型。
- 技能,在整个运行期间安装:Superpowers (obra/superpowers) 用于规划和审查纪律;Context7 (upstash/context7) 用于版本精确的 React 19 和 Vite 6 文档,这样 K3 就不再臆造旧的 API。
这就是整个工具链。你将在下面的每个构建中看到它的工作。
最后你将拥有的东西,逐个要素:
- 一个词汇表:每个公司操作系统必须达成一致的类型化名词(构建 0)
- 一个世界:一个正在运营中的种子公司,所以控制台永远不会是空的(构建 1)
- 一个单一事实来源:一个 store,一个 reducer,没有视图拥有状态(构建 2)
- 一个心跳:公司按自己的时钟运行,而不是你的(构建 3)
- 一个记忆:在重新加载后幸存的状态(构建 4)
- 一个操作员界面:一个屏幕回答发生了什么、是否需要我、我该做什么(构建 5)
- 一个关卡:资金和权力的队列等待你的同意(构建 6)
- 一个命令行:类型化的命令在任何模型被调用之前变成真实的行动(构建 7)
- 一个真实的运行时:一个实际接入的智能体,被围栏隔开(构建 8)
这是给谁看的:任何拥有终端、编码智能体,并且有意向同时运行不止一个智能体的人。你不要复制我的文件。你获取原理和提示词,然后你的智能体编写你的文件。
如何阅读:按顺序阅读,进行检查。每个要素都假设它之前的要素已经存在。检查是证明要素存在的证据;跳过它,你就是在传言之上堆砌。
一切之下的底层原则。三条,接下来九个构建中的每个设计决策都是其中之一的实例:
1. 公司就是状态。不是氛围,不是聊天记录。一棵类型化事实的树,每个屏幕都是它的一个窗口,绝不是它的来源。
2. 权力通过关卡流动。任何涉及花费、雇佣、发布或解雇的行为都要排队等待明确的“是”。没有关卡,就没有公司,只有一个带有 UI 的漏洞。
3. 没有被写下来的事情就没有发生。每一次心跳、每一个决定、每一分钱都落在一个仅追加的日志中。日志是公司对自身的记忆。
从这里开始没有长篇大论。
# 先决条件
```
node --version # v20+; 任何现代运行时都可以,这是参考版本使用的
npm --version # 随 node 一起提供
# 生成每个文件的编码智能体:
ls ~/.kimi-code/bin/kimi # 已安装 kimi code cli
kimi login # 一次;在本地存储 oauth 凭据
# 在第一次提示之前在 kimi code 中安装的技能:
# superpowers (github.com/obra/superpowers) 规划 + 审查纪律
# context7 (github.com/upstash/context7) 全新的、版本精确的文档
```
锁定一次运行时:Kimi K3 是生成参考文件的工作层模型。运行不同的模型你会得到不同的文件,这没问题,因为你在构建你自己的,而不是我的。你需要保持不变的是提示词和检查。
# 为什么选择 Kimi K3

Kimi K3 一览:发布规格和公开基准排名,样式经过调整以匹配控制台。
运行时的选择,快速做出。不是炒作,只是权衡。
它是什么
- Moonshot AI 的开源权重模型,于 2026 年 7 月 16 日发布。
- 2.8T 稀疏 MoE(每个 token 激活 896 个专家中的 16 个),100 万上下文,原生视觉。
- 第一个开源 3T 级模型,迄今为止最大的开源权重发布。
- 完整权重将于 7 月 27 日落地;在此之前仅限托管。
它输在哪里(诚实地说)
- 在 Moonshot 自己的发布表上,Fable 5 在 35 项中赢了 22 项;K3 赢了 12 项。
- 在智能指数中排名 189/189(约 57 分,对比 Fable 5 的 60 分和 GPT-5.6 Sol 的 59 分)。
- 在 FrontierMath 第 4 层得分低于 40%,而封闭的前沿模型接近 90%。
- 幻觉率约 51%,所以要在循环中保留一个验证器。
它赢在哪里(正是这个工作负载)
- 在盲测前端代码竞技场排名第一(1,679 分,领先于 Fable 5)。
- 在 Terminal-Bench 2.1 上得分 88.3%;在 SWE 马拉松和长周期智能体编码方面领先。
- “在多步工具调用会话中保持不脱轨”的技能,这决定了一个智能体运行时的成败。
为什么选择它
- 开源权重:权重发布后即可自托管,拥有你自己的运行时。
- 便宜:缓存命中输入 $0.30/M,输出 $15/M,比 Fable 5 便宜约 70%。
- 你整天运行智能体,你不能从供应商那里租用你的运行时、数据和成本曲线。
- 在智能体编码方面足够接近前沿,开源,且便宜 70%,这胜过那些你自己无法运行的租来的基准点。
# 地图
九个要素,以及它们在参考实现中变成的文件。你的文件名会有所不同。你的要素不会变。
```
any-company-os/
scaffold BUILD(本节):运行时 + 严格类型,最小依赖
domain model BUILD 0: 名词 -> src/lib/types.ts
seeded world BUILD 1: 永不空启动 -> src/lib/seed.ts, skills.ts
source of truth BUILD 2: 单一 store -> src/lib/store.tsx
heartbeat BUILD 3: 2.6秒滴答 -> src/lib/sim.ts
memory BUILD 4: 幸存重载 -> store.tsx 中的持久化
operator surface BUILD 5: 驾驶舱 -> src/App.tsx, views/Command.tsx
the gate BUILD 6: 审批收件箱 -> views/Approvals.tsx
command line BUILD 7: 与之对话 -> views/KimiSpace.tsx
real runtime BUILD 8: 围栏智能体 -> server/kimiBridge.ts, vite.config.ts
```
本指南中的所有十个提示词也作为可运行文件在 prompts/ 中提供,每个要素一个,所以 kimi -p "$(cat prompts/00-scaffold.md)" 可以直接开箱即用。
首先,脚手架。原理:依赖越少,谎言越少。公司操作系统的唯一工作是可信的状态,而每个依赖都是你现在必须信任的别人的状态。
通过 kimi -p 发送提示词:
```
im building a company os. one console to run a whole company of humans
and ai agents from. pick me a lean setup: typed language, fast dev
loop, and as close to zero runtime deps as you can get away with. set
up the scaffold and tell me what you put in and why. anything you cant
justify in one line, rip out.
```
(译文:我正在构建一个公司操作系统。一个控制台,用于运营由人类和 AI 智能体组成的整个公司。为我选一个精简的设置:类型化语言,快速开发循环,并尽可能接近零运行时依赖。搭建脚手架并告诉我你放了什么以及为什么。任何你不能用一行话证明合理的东西,都删掉。)
K3 为参考实现生成的产物:React 19 + TypeScript 5.8 strict + Vite 6,以及除 React 之外仅有一个运行时依赖(lucide-react 用于图标)。脚本:dev, build (tsc -b && vite build), preview。
Context7 在这里已经发挥作用了:没有它,K3 搭建的是 React 18 模式;有了它,配置在第一次尝试时就原生了 Vite 6。
检查:安装并进行类型检查,退出码为 0。统计你的运行时依赖;如果你不能用一行话证明每一个的合理性,这个构建就没有完成。
```
npm install && npx tsc --noEmit; echo "exit: $?"
```
# 构建 0:词汇表
为什么,一句话:公司就是状态,所以在任何行为存在之前,公司赖以运行的每个名词都必须只有一个类型定义。
第一性原理。问在任何由人类和智能体组成的公司中不可约地存在什么,你会得到七个名词:
- 一个工作且花钱的执行者
- 一个说明“为什么”的目标
- 一个说明“做什么”的任务,带有状态和所有者
- 一个等待权力的决策(审批)
- 一个花费单位(账本)
- 一个说明“它发生了”的事件(日志行)
- 一个容纳它们所有的容器(公司)
每个公司操作系统都是这七个名词加上观点。先对名词进行类型化,观点就会保持诚实。
通用提示词。注意它命名了名词和对每个名词要问的两个问题,而与我的技术栈无关:
```
ok before any logic i want the nouns. if im running a company of humans
and agents, what are the things that have to exist? actor, goal, task,
approval, money spent, a line in the log, the company holding it all.
model all of it in types, no behavior. for each noun ask two questions:
what does the operator need to see, and what does the system need to
enforce? statuses are closed unions not strings. money and tokens are
numbers not vibes. and if two of my nouns are secretly the same thing,
call it out, dont let me ship a mess.
```
(译文:好的,在任何逻辑之前,我想要名词。如果我运营一家由人类和智能体组成的公司,必须存在哪些东西?执行者、目标、任务、审批、花费的钱、日志中的一行、容纳所有这些的公司。用类型对所有这些进行建模,不要行为。对于每个名词,问两个问题:操作员需要看到什么,系统需要执行什么?状态是封闭联合类型,不是字符串。金钱和代币是数字,不是氛围。如果我的两个名词实际上是同一个东西,指出来,别让我发布一团糟。)
参考实现。K3 输出了 src/lib/types.ts,416 行,零逻辑。承载整个系统的形状,以及在每个形状内部可见的执行问题:
```
export type AgentStatus = "working" | "idle" | "paused" | "blocked" | "offline";
export interface Agent {
id: ID; companyId: ID; name: string; title: string; department: string;
kind: "ai" | "human"; runtime?: string; model?: string; managerId?: ID;
status: AgentStatus; heartbeat: string;
monthlyBudget: number; spent: number; // enforce: spend has a ceiling
successRate: number; tasksCompleted: number;
skills: string[]; color: string; lastHeartbeat?: number;
}
export type ApprovalType = "hire" | "spend" | "override" | "publish" | "terminate";
export interface Approval {
id: ID; companyId: ID; type: ApprovalType; title: string; rationale: string;
requestedBy: ID; amount?: number;
status: "pending" | "approved" | "rejected"; // enforce: three states, no fourth
checks: PolicyCheck[]; // the machine argues its case
decidedAt?: number; decidedBy?: string;
}
export interface ActivityEvent {
id: ID; companyId: ID; ts: number; actorId: ID;
kind: "heartbeat" | "task" | "delegation" | "spend"
| "approval" | "governance" | "goal" | "system";
message: string; amount?: number; // what is not written down did not happen
}
```
Kimi 的处理过程:一次 kimi -p 调用,一次性生成整个文件。
Superpowers 技能在这里证明了自己的价值。它的审查纪律使 K3 附上了一条“你的两个名词重叠”的说明:委派和任务分配几乎是同一个名词,最终被解析为 Task 上的一个 delegatedBy 字段,而不是一个新的接口。这就是提示词最后一句正在发挥实际作用。
检查:类型检查通过,零 any。然后进行名词审计:grep 你的接口,确认七个名词中的每一个都正好有一个家。
```
npx tsc --noEmit && grep -c "^export interface" src/lib/types.ts
```
# 构建 1:世界
为什么,一句话:你无法学会运营一个空公司,所以操作系统必须启动到一个已经处于运行中的世界。
第一性原理。操作员界面通过识别来教学:你看到一个超支的智能体、一个被阻塞的任务、一个待处理的雇佣,你就学会了控件的作用。空状态什么也教不了,而且它还会隐藏渲染错误(一个空列和一个坏掉的列看起来是一样的)。
所以任何公司操作系统都需要一个确定性的种子世界:每次启动都是相同的世界,每个状态都有代表,每个关卡都已经持有一个决策。
通用提示词:
```
take the types we just wrote and fake me a whole company thats already
running, so the app has stuff on screen the second it boots. who works
here? give em real titles, who reports to who, budgets theyve half
burned through, hit rates. whats being worked on rn, whats stuck? what
decisions are sitting in my inbox waiting on a yes? every status in the
model shows up at least once. make it feel like i walked into a live
company at 2pm on a tuesday, not an empty template. no random(), same
world every boot.
```
(译文:拿我们要写的类型,伪造一个已经在运行的完整公司,这样应用程序在启动的瞬间屏幕上就有内容。谁在这里工作?给他们真实的头衔,谁向谁汇报,他们已经花了一半的预算,命中率。现在正在做什么,什么卡住了?什么决定正坐在我的收件箱里等待我的同意?模型中的每个状态至少出现一次。让它感觉像我在周二下午 2 点走进一家活跃的公司,而不是一个空模板。不要 random(),每次启动都是相同的世界。)
参考实现。K3 生成了两个文件。
seed.ts(约 600 行的 fixtures):
- 两家公司,六家以上的公司,每家有六名以上的智能体,带有汇报线和已花费一部分的预算
- 一个从使命到公司到团队的目标树
- 跨越所有六种状态的任务
- 带有策略检查结果的待处理审批
还有 skills.ts,一个可安装能力包的注册表,归功于它们的真实来源:
```
export const SKILLS: Skill[] = [
s("superpowers", "Superpowers", "obra/superpowers",
"https://github.com/obra/superpowers", "developer",
"Battle-tested workflow superpowers: TDD, debugging, planning, and review discipline."),
s("context7", "Context7", "upstash/context7",
"https://github.com/upstash/context7", "developer",
"Pulls fresh, version-accurate library docs into the agent's context window."),
// ...design, marketing, social, finance, operations, legal packs
];
```
有一个值得注意的循环。当生成这个文件时,在我的 Kimi Code CLI 中运行的两个技能,正是这个文件定义的注册表中的前两个条目。
你正在构建的操作系统为它的智能体安装技能的方式,就像你刚才为构建它的智能体安装技能一样。方法就是产品。
这里需要一次重新生成。K3 的第一遍把每个任务都放成了 in_progress,这违反了“每个状态至少一次”的那一行。修复不是编辑 seed;而是把那一行加到提示词中,然后再运行一次 kimi -p。修复提示词,永远不要修复文件。
检查:启动两次,diff 世界。确定性意味着相同。
```
node -e "const {seedState}=await import('./src/lib/seed.ts'); \
console.log(Object.keys(seedState().tasks).length)" # same count, every run
```
# 构建 2:单一事实来源
为什么,一句话:公司就是状态,所以状态只能在一个地方改变,其他一切都是窗口。
第一性原理。每个多智能体仪表板的失败模式都是一样的:五个组件各自持有一个事实的副本,然后发生分歧。解药是结构性的,而不是纪律性的。
一个 store。一个 reducer,一个从 state 加 action 到下一个 state 的纯函数