Install
HypSwarm needs HypGraph's coordination layer and workflows, which ship with hypgraph 0.7.2:
python -m venv .venv && . .venv/bin/activate # .venv\Scripts\activate on Windows pip install --pre hypswarm # pulls hypgraph>=0.7.2; extras: anthropic · bedrock · langchain · pydantic-ai · crewai · otel hypswarm profiles # the five rosters and their workflows
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
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 discipline's workflow — the stages a question passes through
and the gate out of each — and what "complete" means. A name (software) is its newest version,
software@2; software@1 pins the pre-workflow one.
| profile | roster | the stages a question passes through | the record refuses |
|---|---|---|---|
scientific | analyst, stats, critic, lead | conjectured → preregistered → run → evidenced → claimed → reviewed | unbound numbers; "never" without the count; "significant" without the test; a check on stale evidence |
software | implementer, tester, reviewer, lead | implemented (a diff by hash) → verified (tests_pass_current: the instrument, on that head) → claimed → reviewed | "passes" without an instrument run, or on a superseded head; the diff's author reviewing |
trading | quant, risk, lead | thesised → preregistered → backtested (backtested: out of sample, after costs, trials) → claimed → risk-reviewed → positioned → monitored | costless backtests; in-sample "edge", or without trials; a position without a stop; any live order |
knowledge | researcher, fact_checker, lead | sourced (the passage, retrieved, kind) → claimed (sourced) → checked | claims from recall; a source without its passage |
optimization | modeller, solver, verifier, lead | posed (the instance by hash) → modelled → configured → solved → verified → claimed → checked | objectives without feasibility; a solve that uses no instance; "optimal" without a bound, or on a changed instance |
The scheduler issues each question's stage order. It asks the record where every question stands
(stages()) and issues the gates up to the next review as one order's done-when, to the seat the stage
names; a review stage goes to a reviewer on the node the previous gate produced. The run ends settled
when the record says every question is complete — a checked inconclusive answer, a declined thesis
(no_position) or a verified failure count as complete too. Override the roster with your own
seats.toml whenever you like; the profile's rules still apply.
[[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:
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
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.6import 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 HypGraph UI 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_toon 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_filerefuses 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.eventsis the timeline (dispatch, returned, reproduced, settled, rework, escalate, notice, report_missing with the refusal texts).hypgraph --db issue-412.hypgraph serve— the HypGraph UI: the graph, claims with their standing, provenance, the work orders, overclaims refused, and the run replayed live in Live.hypgraph briefcompiles the orchestrator's view;hypgraph orderslists orders;hypgraph check report.mdlints a write-up against the record.GET /api/graph/stages?inv=…— each question's stage and every gate's status;GET /api/graph/completion?inv=…— the profile's criteria and whether each is met; the record verifies by hash (hypgraph verify).res.reportcarries the workflow's section: the stage per question and the evidence that has gone stale.
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 first stage order asks the implementer for the
change as an artifact kind=diff (base and head sha, files); the tester registers pytest commands
as code with uses=[the diff]; the reproducer runs them in a clean checkout and the lab run carries the same
uses; tests_pass_current is met only by that run, on that head. A later commit supersedes the
diff and the gate reopens. Completion: every requirement's claim rests on a passing instrument run on the current
head — or is a verified failure the record holds. 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 stage orders ask the quant for the thesis as a hypothesis, the
backtest preregistered as an experiment (windows, costs, configurations), then the result with out-of-sample return,
costs and trials; the record refuses a costless result and an in-sample "edge"; the risk seat reviews
against the code; then a decision based_on the claim — kind=position with size, stop,
review date, paper, or kind=no_position. Completion: questions settled, a position taken or declined,
nothing past its review date. 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 part_of the question (citation,
URL/DOI, the passage, retrieved, kind) and claims that cite them; the fact-checker opens the sources; the report
is linted so no sentence treats an unreviewed claim as settled. A question no source answers ends as a checked
inconclusive claim derived from the search's own result. Completion: every claim cites a source and is
checked. 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 instance is registered as an artifact kind=instance; the
modeller writes the model as a hypothesis and the solve configuration as an experiment; the solver's run
uses the instance and its values carry objective, feasible, gap and runtime; the verifier reviews the
result, recomputing feasibility, before the claim; "optimal" is accepted only with a bound that meets the
objective, and never on an instance that has since changed. Completion: a feasible incumbent with a bound, verified.
Scientific — an eval report that holds up
profile scientific tools files, python
Mission: does model A beat model B on an item set? The stage order asks for a hypothesis, an experiment with its expected outcome before the run, then the run; 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="[runputs 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.
pytestruns every scheduler path on scripted models in seconds;bench/stub_bench.pyreports calls, rounds and wall time per run so a change is measured before it is described.