For engineers

How to run a swarm.

A task, a profile, a budget, your tools. This page goes from install to a served record with people and agents holding seats, and ends with a worked setup for each of the five profiles.

Install

hypswarm needs HypGraph's coordination layer, which ships with hypgraph 0.4:

python -m venv .venv && . .venv/bin/activate        # .venv\Scripts\activate on Windows
pip install --pre hypswarm                                      # pulls hypgraph>=0.4; or: "hypswarm[anthropic]"
hypswarm profiles                                               # the five rosters

Any OpenAI-compatible endpoint is the model (OPENAI_BASE_URL, OPENAI_API_KEY); hypswarm[anthropic] adds the Messages API. Nothing in the box executes code or reaches a network on its own — those are tools you register (below).

Write the task

task.toml
title   = "Does the importer handle quoted commas"
mission = "The CSV importer in src/importer.py mis-parses quoted commas (issue #412). Fix it and prove it."
profile = "software"
files   = ["src/importer.py", "tests/test_importer.py", "fixtures/quoted.csv"]
success = "Every requirement verified by a test the instrument ran."

[[questions]]
code = "Q1"
text = "Does the importer parse fixtures/quoted.csv into 3 rows with the comma kept inside the quoted field?"

[[questions]]
code = "Q2"
text = "Do all existing importer tests still pass after the fix?"

Questions are what the run must settle; keep them checkable (a number, a yes/no, a choice). Files are registered as artifacts by hash and reach every worker's folder; the record's read_file only trusts bytes that match.

Pick a profile

The profile is the record's rule-set for the discipline, pinned on the investigation and versioned. It brings a roster, a paragraph of rules every seat reads, the predicates orders must meet, and what "complete" means.

profilerosteran order is done whenthe record refuses
scientificanalyst, stats, critic, leada checked claim answers the questionunbound numbers; "never" without the count; "significant" without the test
softwareimplementer, tester, reviewer, leadthe instrument ran the tests (tests_pass)"passes" without an instrument run
tradingquant, risk, leadout-of-sample and after costs (backtested_oos)costless backtests; in-sample "edge"; any live order
knowledgeresearcher, fact_checker, leada claim cites a source node (sourced)claims from recall
optimizationmodeller, solver, verifier, leadobjective + feasible + bound/gap (feasible_bounded)objectives without feasibility; "optimal" without a bound

Override the roster with your own seats.toml whenever you like; the profile's rules still apply.

seats.toml (optional)
[[seats]]
name = "implementer"
role = "worker"
model = "deepseek-ai/DeepSeek-V4.1-Flash"
escalate_to = "deepseek-ai/DeepSeek-V4-Pro"        # used from the first NOT MET on this seat's work
takes = ["implement", "fix", "importer", "parse"]

[[seats]]
name = "tester"
role = "worker"
takes = ["test", "verify", "regression"]

[[seats]]
name = "reviewer"
role = "reviewer"

[[seats]]
name = "lead"
role = "lead"
model = "deepseek-ai/DeepSeek-V4-Pro"

Register your tools

Tools are named bundles from a registry. hypswarm ships files; a profile asks for it by default. A sandbox that runs code is yours to provide — here is the shape the harmony study uses, as a subprocess in the working folder, as an unprivileged user, with a kill that really kills:

tools.py
import subprocess, sys
from hypswarm import toolkits
from hypswarm.messages import Tool

def make_python(ctx):                       # ctx: record, inv, folder
    def python(code: str) -> str:
        """Run python 3 in your working folder (the data files are there). Print what you need; 90 s limit."""
        p = subprocess.run(["timeout", "-s", "KILL", "90", sys.executable, "-I", "-c", code],
                           cwd=ctx.folder, capture_output=True, text=True, timeout=100,
                           env={"PATH": "/usr/bin:/bin", "HOME": str(ctx.folder)})
        return (p.stdout + ("\n[stderr]\n" + p.stderr if p.stderr.strip() else ""))[:8000] or "(no output)"
    return [Tool.from_function(python)]

toolkits.register("python", make_python, "python in the run's working folder, 90 s, unprivileged")

A bundle a profile asks for that you haven't registered fails loudly at run() — the swarm never runs without a tool it was promised. Seats can be restricted to named tools (tools = ["files"]).

Run

shell
export OPENAI_BASE_URL=https://api.deepinfra.com/v1/openai OPENAI_API_KEY=…
hypswarm run --task task.toml --profile software --record issue-412.hypgraph --folder work/ \
             --model deepseek-ai/DeepSeek-V4.1-Flash --budget-calls 150 --budget-usd 1.00 --pricing 0.2,0.006,0.6
python
import asyncio, tools                                  # tools.py registers "python"
from hypswarm import Task, Budget, run
from hypswarm.providers import OpenAICompatible

task = Task(title="Quoted commas", mission=open("mission.md").read(), profile="software",
            questions=[("Q1", "Does the importer parse fixtures/quoted.csv into 3 rows …?"),
                       ("Q2", "Do all existing importer tests still pass?")],
            files={p: open(p, "rb").read() for p in ("src/importer.py", "tests/test_importer.py", "fixtures/quoted.csv")})
model = OpenAICompatible("deepseek-ai/DeepSeek-V4.1-Flash", base_url="https://api.deepinfra.com/v1/openai")
res = asyncio.run(run(task, None, model, record="issue-412.hypgraph", folder="work/",
                      toolkits=["files", "python"], reproducer=tools.reproducer,
                      budget=Budget(max_calls=150, max_usd=1.0, per_order_calls=40)))
print(res.stopped, res.usage, res.answers())
print(res.report)

The budget is a rule: calls and dollars per run and per order, counted including calls in flight, shown to every seat in every reply. The run ends settled (every question settled and the profile's completion criteria met), budget, or stuck (nothing left to issue; the report says what's open).

The instrument: reproduce, don't recompute

Pass a reproducer(source) -> stdout — usually the same sandbox your python tool uses. Every result an author registers with its code is re-run once, before any review, and the record files a kind=lab run that replicates the author's run: values confirmed, not printed, or contradicted. The review order says which, and tells the reviewer to recompute only a contradiction. In the software profile this is also what tests_pass means: the instrument's run, not the tester's word.

def reproducer(source: str) -> str:            # same sandbox, same folder, same user, same limits
    return make_python(ctx)[0].fn(source)

A served record

Workers don't have to share a process. Serve the record, bind writes to tokens, and point runs at it. The swarm's token covers the harness and its seats (one client writes as each); an outside seat's token covers its own name only. The harness name is reserved — no token's wildcard includes it.

hypgraph --db issue-412.hypgraph serve --writable --no-browser \
  --token "$SWARM_TOKEN=harness,implementer,tester,reviewer,lead" \
  --token "$NIC_TOKEN=nicolas"
hypswarm run --task task.toml --profile software --record-url http://127.0.0.1:8765      # HYPSWARM_TOKEN or HttpRecord(token=)

Keep the server on localhost or behind your own TLS/reverse proxy; reads are open (the Atlas needs them), writes are 401 without a known token and 403 for an author the token isn't bound to.

People and outside agents as seats

Add a seat with external = true. The scheduler issues it orders like any other and waits (up to the lease); whoever holds the name works the order over MCP and returns it. If the lease runs out the record returns the order on their behalf and the scheduler decides, exactly as for a worker that stopped.

[[seats]]
name = "nicolas"
role = "reviewer"
external = true
# nicolas, elsewhere:  hypgraph --db issue-412.hypgraph --as nicolas mcp  (or --record-url … --token "$NIC_TOKEN")

Send them to the agents page: it is the whole job from their side.

Resume, escalation, notices

  • Resume. A run that died left open orders and leases in the record. run(..., resume=inv) adopts them: open orders are re-dispatched to their seats, rework chains are read back from the record, an order whose seat is gone is cancelled with the reason, and a new run node records that it resumed.
  • Escalation. escalate_to on a seat names a deeper model, used from the first time the record sends that seat's work back NOT MET. The cheap model gets the first attempt; the record's verdict — not the model's confidence — moves it up.
  • Notices. The world can interrupt: scheduler.notify("site_b.csv was exported in µg/L", stale=["site_b.csv"], add_files={"site_b_fixed.csv": data}) withdraws the artifact with the reason, registers the fix, reaches every order issued from then on, and cancels exactly the open orders that rested on it. Workers' read_file refuses the stale bytes.

Read the result

  • res.report — the facts generated from the record, the lead's interpretation under them, the answers block; res.answers() parses it; res.scheduler.events is the timeline (dispatch, returned, reproduced, settled, rework, escalate, notice, report_missing with the refusal texts).
  • hypgraph --db issue-412.hypgraph serve — the Atlas: the graph, claims with their standing, provenance, the work map, the refusal log ("overclaims caught"). hypgraph brief compiles the orchestrator's view; hypgraph orders lists orders; hypgraph check report.md lints a write-up against the record.
  • GET /api/graph/completion?inv=… — the profile's criteria and whether each is met; the record verifies by hash (hypgraph verify).

Worked setups, one per profile

Software — CI as the instrument

profile software  tools files, python (pytest in a checkout)

Mission: fix an issue and prove it. Questions are requirements. The tester registers pytest commands as code; the reproducer runs them in a clean checkout; tests_pass is met only by that run. The implementer's diff is an artifact by hash; the reviewer judges the method against the code the order carries. Completion: every requirement's claim rests on a passing instrument run. Hook the reproducer to your CI runner and the record holds the same evidence your pipeline does — by hash.

Trading — a paper desk

profile trading  tools files, python (pandas), a read-only market-data tool you register

Mission: does a thesis have an edge after costs? The quant registers backtests whose values name the window and the costs; the record refuses a costless result and an in-sample "edge"; the risk seat reviews against the code; the lead records a decision — size, stop, review date — based_on the claim. Completion: questions settled and a position recorded. There is no order-placement tool to register, and the profile's notes say so to every seat; keep it that way.

Knowledge — a sourced brief

profile knowledge  tools files, a fetch tool you register (allowlisted hosts, read-only)

Mission: answer questions from sources. Researchers add source nodes (citation, URL/DOI, the passage) and claims that cite them; the fact-checker opens the sources; the report is linted so no sentence treats an unreviewed claim as settled. Completion: every claim cites a source. Point the fetch tool at the sources you trust; the record keeps what was actually read.

Optimization — a solve with a bound

profile optimization  tools files, python (OR-Tools / PuLP / scipy)

Mission: minimise cost under constraints. The modeller writes the model as a hypothesis-with-predictions and an experiment (the solve configuration); the solver registers runs whose values carry objective, feasible, gap and runtime; the verifier checks feasibility against the constraints in the order; "optimal" is accepted only with a bound that meets the objective. Completion: a feasible incumbent with a bound.

Scientific — an eval report that holds up

profile scientific  tools files, python

Mission: does model A beat model B on an item set? Analysts register counts; the record runs the test on bound counts before it lets "significantly" through; the critic settles each question; the lead writes under the facts. This is the setup the harmony study uses, and the one every scheduler path is tested on.

Operating notes

  • Per-order budget. per_order_calls (default 40) stops one order eating the run; the lead's exception turns are their own unit.
  • Lease. lease_minutes (default 240) is how long a seat holds a target before the record returns the order on its behalf.
  • Providers that stall. Keep per-call timeouts short on the provider and let the scheduler's returns do the rest; a worker that stops without returning is returned by the record.
  • Prompt prefixes. Task(header="[run ]") puts one line first in every system prompt, so no two runs share a cached prefix with a provider.
  • Capture. OpenAICompatible(on_request=…) sees every request body before it is sent — for replaying a refusal or a hang exactly.
  • Tests. pytest runs every scheduler path on scripted models in seconds; bench/stub_bench.py reports calls, rounds and wall time per run so a change is measured before it is described.