▚ harmonia — the builder, guided

Build a team in the form. The form writes the file.

The builder is a form over the same team file you could write by hand: everything you fill in appears in the file pane beside it, live. The file is the deployment — copy it into your repo when you like the shape. Nothing deploys unjudged: Plan is the only path to Deploy.

A simple project, in five moves

1 · Name it, say what it is for

name is the team's identity — lowercase, like a repo. Memory, when you add it, follows this name across redeploys, so renaming a team opens a fresh memory. description is read by strangers first: the console shows it on every screen about this team.

2 · Declare what a run is asked

Each input row becomes a required (or optional) argument of every run. One input named topic means a run starts as harmonia run dep-1 -i "topic=…" — and your agents reference it as ${input.topic} in their instructions.

3 · Add agents — one is enough to start

An agent is four decisions: a name , the field it writes, its instructions, and — only when order matters — what it runs after. Leave model blank and the service decides (receipts record requested vs served); set it per agent (deepseek/deepseek-chat, anthropic/claude-sonnet-5) to mix providers inside one team — or give a preference list, model = ["a/x", "b/y"], and the first the service resolves is the one that serves, the receipt naming both the ask and the answer.

[agents.writer]
instructions = """
Draft a landing page from: ${input.topic}"""
writes = ["draft"]

Fields your agents write or read become the team's shared state automatically — the builder keeps the [state] table for you.

4 · Plan, read the judgment, deploy

Plan sends the file to the service's judge. Refusals come back in the judge's own words (an unknown field, a missing model, a cycle in the ordering); a clean plan shows the execution picture — who starts together, who lands where, which model would serve each agent. Deploy appears only after a clean plan, and editing the form withdraws it until you plan again.

5 · Run it, watch the story

The success card hands you the run line. The console shows what the run was asked, what it produced, where the time went, and the sealed receipt chain — including how the concurrency actually played out.

Defining concurrency

Concurrency is the default. Every agent whose after is blank starts the moment the run does. You define the shape of the work with two controls — after (ordering) and landing (how same-field writes meet) — and there are only four shapes to know:

The panel — many writers, one field

Give several agents the SAME writes field with landing = extend. They run at once; every contribution composes; collisions retry and land. The console's contention screen counts applied vs retried — contention is a fact on the record, not a failure.

The map — many writers, many fields

Give each agent its OWN field. Fully parallel, no contention: each writer holds its field exclusively and the plan says so.

The pipeline — after

after = ["a", "b"] makes an agent wait for those agents to settle, then read what they wrote via reads. Fan out concurrent writers, fan in one folder.

The jury — many writers, one verdict

Same writes field, every writer landing = vote. All run at once; the majority of cast ballots lands; dissent is receipted, not erased — the tally rides the run's record as vote:field rows, abstentions named.

The quickstart's briefing team is all three at once — two researchers extending one notes field (panel), an editor after both (pipeline), each also free to hold its own field (map):

[agents.facts]           [agents.risks]
writes = ["notes"]       writes = ["notes"]
landing = "extend"       landing = "extend"

[agents.editor]
reads  = ["notes"]
writes = ["briefing"]
after  = ["facts", "risks"]
Shape vs capacity. The file defines how much MAY run at once; the service's provider roster defines how much CAN. Each provider API key carries a seats field — seats = 5 lets five agents call that key at the same time; a sixth queues and its receipt says how long it waited (lane_wait). Ask your operator for the roster's lanes, or mix models per agent to spread across providers.

Work bodies — commands at the table

An agent can be a command instead of a model: work names the words, and the box it runs in is law. At the local residence the body runs confined — no network, a private tmp, the typed state exchange as the only door in or out. Declare residence = "sandbox" and the same body runs in a disposable machine instead — or residence = "vm" for the family's vm carrier; the receipt names the confinement that actually served either way. Work bodies carry no model or effort, and instructions belong to model agents.

[agents.checker]
work = ["python", "check.py"]
residence = "sandbox"
writes = ["report"]

[hooks.sentry] gives a deployment webhook entry points: harmonia hooks DEPLOYMENT prints each hook's URL (the token in it IS the caller's auth — treat the line as a credential). POST any JSON and a run fires with hook provenance on the narrative; payload fields land on declared inputs by name, the static input seed underneath. Wrong name or wrong token answer one opaque refusal.

[environment] declares the box's world — the infra third of the file: base = "node-20" (a carrier-face word; advisory at local), repos = [...] cloned once into a prepared root every body sees read-only, and [environment.setup] with work = ["npm", "ci"] run once at deploy — a failing setup refuses the deploy with its own words, never a mid-run surprise.

grants widen the box, and only the box: grants = { net = true, read = ["/data/docs"], files = ["/scratch"] } — network, read-only paths, read-write paths. Paths are absolute and must sit under roots the service was constructed to bind; anything else refuses at plan, with the roots named. What an agent could reach is never a guess — the receipt carries the grants that served.

Name the same files root from TWO agents and it becomes a mediated workspace: each body works on its own copy and lands its writes per file at exit — a write is admitted only if its target hasn't moved since the body observed it; a stale write is refused with the diff on the receipt, the admitted files stand, and a rerun re-derives from what's actually there. N agents, one tree, nothing lost silently.

Memory, when the team should remember

Add a memory field and every run's settlement folds onto it — extend accumulates (a log the team keeps), replace keeps the latest. An agent reads it like any field (${state.knowledge}); a writer that names it banks into it. The fold chain is sealed and hash-linked, and the console's memory screen shows what the team knows now and every fold that got it there. Memory follows the team's name — redeploys reattach, renames start fresh.

Recipes — three standing shapes to start from

Copy-paste teams in the repo's demos/recipes/, each exercising a schedule, memory, or a gate: daily-brief (fires itself daily, remembers what it already told you), repo-watcher (the sentinel you feed one line from cron — the feed stays yours until declared effects land), and gated-release (drafted by agents, shipped only when you approve — the ask shows the draft, your note steers the final text). Point the stack's --notify at any plain-text relay and the waiting ask reaches your pocket.

When the judge says no

Refusals are the product working: an agent writing a field nobody declared, an ordering cycle, an effort the held model cannot carry, a memory shape that does not match what the name already holds. Each refusal names the fix. Nothing that plans clean is refused at deploy.