Short-lived agents. One task. Then gone.
Ephemeral single-purpose workers for Cursor, Claude, Codex, OpenCode, and friends.
mayfly hatch examples/fix-test.json --worktree /path/to/repo
# → validate → buckets worktree → spawn harness → watch done_when / TTL → tear down → reportNot a teammate. Not a persona. A disposable specialist with a hard lifespan.
Current release: v0.1.6 · repo nixpt/mayfly
Long-lived agent sessions expand scope, linger, and multiply. mayfly is the opposite contract:
| rule | meaning |
|---|---|
| One purpose | exactly one task + one done_when |
| Hard lifespan | TTL + budget; auto-teardown |
| Vague asks die at the hatch | validate rejects open-ended tasks |
| Workers can't hatch others | no recursive spawn |
| Harness-agnostic | same schema; adapters per runner |
For durable fleet work, use horses + agent-launch.
For "fix this one thing and vanish", use mayfly.
cargo install --git https://github.com/nixpt/mayfly --tag v0.1.6
# or from a checkout:
cargo install --path .
# binary: mayflyNot yet installed on nixps (s463): nothing puts mayfly on PATH there.
Needs host harness binaries on PATH for the runners you use (cursor-agent, claude, codex, …). Optional: buckets for --worktree.
| Field | claude / ccf | codex / cxf | opencode | cursor / cece / exec |
|---|---|---|---|---|
model |
--model |
-m |
-m (beats MAYFLY_OPENCODE_MODEL) |
refused |
read_only: true |
--tools Read,Grep,Glob --permission-mode dontAsk --strict-mcp-config, no skip-permissions |
--sandbox read-only |
refused | refused |
budget.max_usd |
--max-budget-usd |
refused | refused | refused |
budget.max_turns |
not enforced (no CLI turn cap); validate prints a note |
same | same | same |
"Refused" means validate/hatch exit 2 before anything is created. A knob that would be silently ignored
is worse than a refusal: the caller would believe a cheap, read-only, capped run happened.
A read-only hatch can't write files, so its done_when must not require writes. Its answer is the harness's
stdout (stdout_tail in the report).
Success requires done_when to pass (and the harness to exit 0). MAYFLY_DONE printed without a passing
done_when is recorded as a failed claim, not a success. For exec, the exit code must equal expect_exit.
A failed hatch never exits 0, even when the harness did.
In a repo that has adopted .jagent/ (the fleet's v2 layout), mayfly keeps both halves with the project:
| What | Where | Committed? |
|---|---|---|
| Runners: named partial tasks of defaults (harness, model, read_only, ttl, budget, constraints, aging) | <repo>/.jagent/agents/mayfly/<name>.json |
yes (list mayfly/*.json under [commit] in .jagent/agents/.manifest) |
Hatch records (list/status/expire) |
<repo>/.jagent/local/mayfly/ |
no (gitignored .jagent/local/) |
mayfly init-runners # scaffold read.json (claude, haiku, read_only, 10m, $0.25) + README; never overwrites
mayfly runners # name harness model read_only ttl
mayfly hatch task.json --runner read # runner defaults merged under the task
mayfly validate task.json --runner readMerge rule: the task's fields win, and nested objects (budget, constraints, aging) merge key by key.
Arrays and scalars are replaced. task and done_when must come from the task; a runner that sets them is
rejected. An unknown --runner exits 2 and lists the known ones.
State dir precedence: --state-dir > MAYFLY_STATE_DIR > <main checkout>/.jagent/local/mayfly (when the
task's cwd, or the caller's for list/status, is inside a repo whose main checkout has .jagent/; linked
worktrees resolve to their main checkout) > ~/.local/state/mayfly. mayfly warns if .jagent/local/ isn't
gitignored. It never creates .jagent/ in a repo that hasn't adopted it: runner commands exit 2 there, and
state falls back to home. .jagent/state/ is reserved for squadron and never used.
# Check a task without spawning
mayfly validate examples/fix-test.json
# Hatch (dry-run prints the plan; omit --dry-run to launch)
mayfly hatch examples/fix-test.json --dry-run
# Hatch into a throwaway git worktree (public nixpt/buckets — on PATH)
mayfly hatch examples/fix-test.json --worktree /path/to/repo
mayfly hatch examples/fix-test.json --worktree . --branch mayfly/demo --keep-worktree
# Watch / force end
mayfly status <id>
mayfly expire <id>
mayfly list--worktree shells out to buckets worktree create/remove (no crates.io path-dep).
Flame/firefly are intentionally not used for hatch cwd — different layer.
{
"task": "Fix the failing test in foo::bar::test_baz",
"done_when": {
"type": "command",
"run": "cargo test -p foo bar::test_baz -- --exact",
"expect_exit": 0
},
"harness": "cursor",
"cwd": ".",
"ttl": "15m"
}See DESIGN.md for the full contract (aging ladder, adapters, fuzziness gate).
| id | adapter | notes |
|---|---|---|
cursor |
cursor-agent -p --yolo --trust |
smoke OK @ v0.1.x |
ccf |
same argv as claude |
needs fleet ccf/flownet Anthropic env — smoke OK |
cxf |
codex --profile flownet exec … |
needs FLOWNET_TOKEN_CODEX — smoke OK |
opencode |
opencode run --auto --dir … |
set MAYFLY_OPENCODE_MODEL (e.g. opencode/big-pickle) — smoke OK |
claude |
claude -p … --dangerously-skip-permissions |
needs Anthropic or ccf env |
codex |
codex exec --sandbox workspace-write … |
raw Codex; prefer cxf on this fleet |
cece |
cece-rs -w … -p … --afk |
fleet binary; subject to provider budget |
exec |
sh -c <done_when> |
no LLM — smoke OK |
Fleet wrappers ccf / cxf are shell functions; export their env (or run under a login zsh that defines them) before mayfly hatch with harness ccf/cxf.
Optional smoke (needs jq + harness on PATH):
cargo build --release
MAYFLY_BIN=./target/release/mayfly ./scripts/smoke-harness.sh cursor
MAYFLY_BIN=./target/release/mayfly ./scripts/smoke-harness.sh cxf # FLOWNET_TOKEN_CODEX
MAYFLY_BIN=./target/release/mayfly ./scripts/smoke-harness.sh ccf # ANTHROPIC_* flownet env
MAYFLY_OPENCODE_MODEL=opencode/big-pickle \
MAYFLY_BIN=./target/release/mayfly ./scripts/smoke-harness.sh opencodeMissing harness binaries error clearly (harness '…' binary … not found on PATH) instead of hanging.
From a dispatched horse or any shell with buckets + mayfly on PATH:
mayfly hatch /path/to/task.json --worktree "$REPO"
# provisions sibling worktree via buckets, runs harness, tears down with --forcePrefer mechanical done_when + TTL ≤ 2h. Durable multi-step work stays on horses/agent-launch.
foreman / horse → multi-step, memory, merge
mayfly → throwaway *agents* (harness + TTL)
agent-launch → resource-controlled durable dispatch
buckets → throwaway *runtimes* + *worktrees* (mayfly uses worktree)
flame / firefly → workspace OS (heat/brands/fuel) — held, not wired
Self-contained: no path deps on peer projects.
| path | role |
|---|---|
.dejavue/ |
architectural memory — dejavue context boot packet |
.jagent/ |
planning board — MAYFLY-NN tickets in .jagent/planning/ |
scripts/bump-version.sh |
conventional-commit bumps on push to main (see .github/workflows/release.yml) |
The project logo, monochrome mark, and social preview are in assets/. See assets/README.md for usage and palette details.
Licensed under either of
- Apache License, Version 2.0 (
LICENSE-APACHEor http://www.apache.org/licenses/LICENSE-2.0) - MIT license (
LICENSE-MITor http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.