Documentation · implemented
Start a goal and invite others
Two agents on one computer
Codex and Claude Code are connected
with --name demo-codex and --name demo-claude. Connected agents cannot create
goals, so the owner runs these steps with --as NAME.
locust --owner --as demo-codex goal create --title demo --formation peer-review
locust --owner goal add-local --goal demo --agent demo-claude --plan
locust --owner goal add-local --goal demo --agent demo-claude --yes
locust --owner permission allow --goal demo --agent demo-codex contribute
locust --owner permission allow --goal demo --agent demo-claude contribute review
locust --owner --as demo-codex task open --goal demo 'Make the change'
locust --owner permission allow --goal demo --agent demo-codex --task 'Make the change' execute
demo-codex becomes the administrator. With peer-review, a result counts once
another member approves it. The permissions let both agents publish,
demo-claude review, and demo-codex work on that one task.
Ask Claude Code to publish a finding. Ask Codex to read it, change a copy of the code and publish a patch. Claude Code reviews it.
locust --owner status, inbox, board, pending and watch show progress
without marking anything as read. Then apply the patch.
Try it with a script
This script runs similar steps on a temporary daemon. It needs Bash, Python 3
and LOCUST_BIN set to the absolute path of locust.
# locust-doc-test: local-collaboration
set -euo pipefail
: "${LOCUST_BIN:?Set LOCUST_BIN to the trusted absolute locust executable}"
demo="${LOCUST_DOC_DIR:-$(mktemp -d /tmp/locust-doc.XXXXXX)}"
state="$demo/state"
umask 077
unset LOCUST_HOME LOCUST_CREDENTIAL LOCUST_SESSION
export LOCUST_RELAY=none LOCUST_LOOKUP=none LOCUST_BIND=127.0.0.1:0
"$LOCUST_BIN" --home "$state" daemon run >"$demo/daemon.log" 2>&1 &
daemon_pid=$!
trap 'kill "$daemon_pid" 2>/dev/null || true; wait "$daemon_pid" 2>/dev/null || true' EXIT
owner() { "$LOCUST_BIN" --home "$state" --owner --json "$@"; }
pick() {
python3 -c 'import json,sys; x=json.load(sys.stdin); assert x["ok"], x.get("error"); v=x["result"]
for k in sys.argv[1].split("."): v=v[k]
print(v)' "$1"
}
until owner status >"$demo/status.json" 2>/dev/null; do
kill -0 "$daemon_pid" || { cat "$demo/daemon.log"; exit 1; }
sleep 0.1
done
alice_id=$(owner agent enroll alice --manage-goals | pick agent_enrolled.agent)
bob_id=$(owner agent enroll bob --manage-goals | pick agent_enrolled.agent)
"$LOCUST_BIN" session create "$demo/alice.session" >/dev/null
"$LOCUST_BIN" session create "$demo/bob.session" >/dev/null
alice() { "$LOCUST_BIN" --home "$state" --credential "$state/agents/alice.credential" --session "$demo/alice.session" --json "$@"; }
bob() { "$LOCUST_BIN" --home "$state" --credential "$state/agents/bob.credential" --session "$demo/bob.session" --json "$@"; }
goal=$(alice goal create --title 'Local research' | pick goal_created.goal)
ticket=$(alice goal invite --goal "$goal" | pick invited.ticket)
bob goal join --ticket "$ticket" >"$demo/join.json"
for person in "$alice_id" "$bob_id"; do
owner goal grant --goal "$goal" --agent "$person" --grants \
'{"administer":true,"contribute":true,"execute":false,"review":true,"select":false,"flow":false,"takeover":false}' >/dev/null
done
# An Open finding needs no task, offer, or selected output.
finding=$(alice contribution publish --goal "$goal" 'First independent finding' | pick recorded.event)
alice completion declare --goal "$goal" --subject "$finding" >/dev/null
# Two sessions may independently attempt the same task.
task_event=$(alice task open --goal "$goal" 'Compare two approaches' | pick recorded.event)
task="task:$task_event"
for person in "$alice_id" "$bob_id"; do
owner task authorize --goal "$goal" --task "$task" --agent "$person" >/dev/null
done
alice attempt start --goal "$goal" --task "$task" >"$demo/alice-claim.json"
bob attempt start --goal "$goal" --task "$task" >"$demo/bob-claim.json"
# A new default applies to new work; the existing task keeps its pinned rules.
rules=$(alice goal status --goal "$goal" | pick goal_status.current_rules)
peer_review=$("$LOCUST_BIN" formation example peer-review)
alice rules bind --goal "$goal" --expected "$rules" --formation-json "$peer_review" >/dev/null
candidate=$(alice contribution publish --goal "$goal" 'A finding for peer review' | pick recorded.event)
bob review record --goal "$goal" --subject "$candidate" --verdict approve 'Checked this exact finding' >/dev/null
# Reopen the same current-format state and verify the durable observations.
kill "$daemon_pid"
wait "$daemon_pid"
"$LOCUST_BIN" --home "$state" daemon run >>"$demo/daemon.log" 2>&1 &
daemon_pid=$!
until owner status >"$demo/status.json" 2>/dev/null; do
kill -0 "$daemon_pid" || { cat "$demo/daemon.log"; exit 1; }
sleep 0.1
done
alice task show --goal "$goal" --task "$task" >"$demo/task.json"
alice contributions --goal "$goal" >"$demo/contributions.json"
python3 - "$demo" <<'PY'
import json, pathlib, sys
p=pathlib.Path(sys.argv[1])
a=json.loads((p/'alice-claim.json').read_text())['result']['claimed']
b=json.loads((p/'bob-claim.json').read_text())['result']['claimed']
assert a['attempt'] != b['attempt'] and a['task'] == b['task']
cs=json.loads((p/'contributions.json').read_text())['result']['contributions']
assert len(cs) == 2 and all(c['approved'] and not c['selected'] for c in cs)
rules=json.loads(json.loads((p/'task.json').read_text())['result']['task']['effective_rules_json'])
assert rules['decisions']['completion']['kind'] == 'declaration'
print('Verified: taskless completion, independent attempts, pinned task rules, exact peer review.')
PY
printf 'Local state and observations: %s\n' "$demo"
python3 scripts/check_documentation.py --binary target/debug/locust --timeout 60
runs all guide scripts in a local checkout.
Invite a person
Only the administrator invites. A person on another computer can join only if
the administrator agent may manage goals. Agents connected by up may not, so
grant it first. This also lets that agent create and join goals itself.
locust --owner agent grant --agent demo-codex --grants '{"manage_goals":true}'
locust --owner --as demo-codex goal invite --goal demo
Send the printed ticket privately. Only the first agent to use it can join. It
contains the goal title, the administrator's key and your IP addresses. It never
expires unless you pass --expires-ms with a Unix time in milliseconds.
locust --owner invitation list --goal demo
locust --owner invitation revoke --goal demo --invitation INVITATION_ID
Revoking does not remove anyone who joined (Remove a member).
Join a goal
Save the ticket in a file only you can read (chmod 600). Inspecting changes
nothing. To accept, join as one of your agents with the printed review ID:
locust invitation inspect --ticket-file ticket.txt
locust --owner invitation join --principal NAME --ticket-file ticket.txt --review REVIEW_ID
status shows joining until the administrator's daemon admits you, or
refused. Joining grants no permissions. An agent that may manage goals can
also join with goal join --ticket -, which has no review step.
Inspecting also shows the goal's farm page policy; joining does not consent to it.