Skip to main content

A local-first free-form task inbox daemon for concurrent coding agents, Git worktrees, and maintainer-style integration.

Project description

Alluvium

Alluvium is a local-first task inbox daemon for running coding agents concurrently.

You drop free-form folders or bare files into tasks/inbox/. Alluvium assigns each item a unique task ID, runs an agent in an isolated Git worktree/branch, stores task-local artifacts under .agent/, and serially integrates any durable repository changes.

The CLI is alluvium. The PyPI distribution is alluvium-swarm because the bare alluvium package name is already taken.

Quick start

Install:

uv tool install alluvium-swarm

Create a workspace:

mkdir my-alluvium
cd my-alluvium
alluvium init

Start the daemon in the background:

alluvium daemon

Drop work into the inbox:

echo "Please summarize this." > tasks/inbox/request.txt

Check progress:

alluvium status
find tasks -maxdepth 3 -type f | sort

Stop or reload:

alluvium reload        # reload config.toml
alluvium stop-daemon   # graceful stop

For debugging instead of background mode:

alluvium daemon --foreground

What gets created

my-alluvium/
  config.toml
  tasks/
    inbox/          # public drop zone: folders or bare files
    running/        # currently being worked
    needs_revision/ # integrator sent task back to worker for amendment
    needs_human/    # blocked on clarification/approval/manual intervention
    done/           # worker finished; integration status is in .agent/integration.json
    failed/         # worker failed
    dead_letter/    # reserved for invalid/repeatedly failed tasks
  repo/             # durable Git repo: code, docs, knowledge, skills, tests, etc.
  worktrees/        # one Git worktree per active task
  logs/
  .alluvium/        # daemon pid/lock files

A bare file is wrapped into a task folder automatically:

tasks/inbox/report.pdf
  ↓
tasks/running/20260516T101530Z-report.pdf-c771aa/
  report.pdf
  .agent/

A dropped folder keeps its contents but receives a unique internal name:

tasks/inbox/acme-contract/
  contract.pdf
  request.md
  ↓
tasks/running/20260516T101530Z-acme-contract-a8f2c1/
  contract.pdf
  request.md
  .agent/

.agent/ is reserved runtime state. If a producer supplies .agent/ in an inbox item, Alluvium quarantines it and creates a fresh trusted .agent/ subtree.

Lifecycle

tasks/inbox/
  ↓
tasks/running/
  ↓
tasks/done/
  ↓ integrator reviews branch
      ├── noop
      ├── merged
      ├── needs_revision → same task/branch is re-run and amended
      └── needs_human

Workers may run concurrently. Integration into repo/main is serialized.

done/ means the worker completed. It does not necessarily mean the branch is already merged. Integration state is stored in:

.agent/integration.json

Examples:

{"status": "noop", "has_repo_changes": false}
{"status": "merged", "has_repo_changes": true, "merge_commit": "..."}
{"status": "needs_revision", "reason": "integration tests failed"}

When revision is needed, the integrator writes:

.agent/revision_request.md

and moves the task to tasks/needs_revision/. The coordinator re-runs a worker on the same task ID, branch, and worktree so it can amend the branch.

Configure a real coding agent

The default worker is a deterministic built-in inventory agent. It is useful for smoke tests, but it does not reason or edit meaningfully.

Edit config.toml:

[agent]
# Placeholders: {task_id}, {task_dir}, {agent_dir}, {worktree}, {branch}, {prompt_file}
command = ["your-coding-agent", "--cwd", "{worktree}", "--prompt-file", "{prompt_file}"]
timeout_seconds = 3600

Alluvium also passes environment variables to the worker:

ALLUVIUM_TASK_ID
ALLUVIUM_TASK_DIR
ALLUVIUM_AGENT_DIR
ALLUVIUM_WORKTREE
ALLUVIUM_BRANCH
ALLUVIUM_PROMPT_FILE

The generated prompt asks the worker to:

  • infer the task from the free-form folder,
  • write .agent/understanding.md, .agent/plan.md, .agent/result.md, and .agent/result.json,
  • put task artifacts under .agent/outputs/,
  • commit useful durable repo changes on the task branch,
  • request human help by writing .agent/needs_human.md,
  • address .agent/revision_request.md when returned by the integrator,
  • log external effects under .agent/effects/ledger.jsonl.

Example: use pi as the worker

[agent]
command = [
  "pi",
  "--model", "gpt5/gpt-5.4:high",
  "--no-session",
  "-p", "@{prompt_file}"
]
timeout_seconds = 3600

Pi runs from the task worktree. The prompt file includes the absolute task folder path, so pi can write .agent/ outputs and edit the durable repo.

Useful commands

alluvium init [path]                 # initialize workspace
alluvium daemon                      # start background daemon
alluvium daemon --foreground         # foreground/debug mode
alluvium reload                      # SIGHUP daemon to reload config.toml
alluvium stop-daemon                 # graceful stop
alluvium stop-daemon --force         # SIGKILL if graceful stop times out
alluvium status                      # JSON status
alluvium run-once --ignore-settle    # process current inbox once, useful for tests
alluvium integrate-once              # run integrator once
alluvium example-task                # create a sample inbox task

Daemon logs:

logs/daemon.log

Daemon pid/lock:

.alluvium/daemon.pid
.alluvium/daemon.lock

Inbox publishing safety

The daemon waits for inbox items to be unchanged for inbox_settle_seconds before claiming them. This makes drag-and-drop/manual copies less likely to be processed halfway through.

For producer programs, prefer atomic publish:

tasks/inbox/.some-task.tmp/
  files...
  ↓ rename when complete
tasks/inbox/some-task/

Bare files are supported too:

tasks/inbox/report.pdf

Durable repo conventions

Alluvium does not impose a special knowledge/proposal topology. The durable repo is just a Git repository. It may contain code, docs, knowledge, skills, tests, generated assets, or anything else your project needs.

Workers should make the branch correct as a whole. The integrator acts like a repo maintainer: it serially reviews, tests, merges, or sends the task back for amendment.

Put project-specific conventions in:

repo/AGENTS.md

Development

git clone https://github.com/spoj/alluvium
cd alluvium
uv sync --dev
uv run pytest
uv run alluvium --help

Build and publish:

uv build
UV_PUBLISH_TOKEN=... uv publish

systemd user service

Use foreground mode under systemd:

[Unit]
Description=Alluvium Daemon
After=network.target

[Service]
Type=simple
WorkingDirectory=/home/me/my-alluvium
ExecStart=/home/me/.local/bin/alluvium daemon --foreground --config /home/me/my-alluvium/config.toml
Restart=always
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=60

[Install]
WantedBy=default.target

Then:

systemctl --user daemon-reload
systemctl --user enable alluvium
systemctl --user start alluvium
journalctl --user -u alluvium -f

Shutdown behavior

alluvium stop-daemon sends SIGTERM. The daemon stops accepting new work immediately, wakes sleeping loops, waits up to shutdown_grace_seconds for active workers, and then cancels remaining worker supervisors. Worker subprocesses receive SIGTERM and then SIGKILL if they do not exit.

If you send kill <pid> manually, the same SIGTERM path is used. If it appears slow, it is usually waiting for active worker cleanup. Use:

alluvium stop-daemon --force

only when you are okay with interrupting active workers.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

alluvium_swarm-0.3.0.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

alluvium_swarm-0.3.0-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file alluvium_swarm-0.3.0.tar.gz.

File metadata

  • Download URL: alluvium_swarm-0.3.0.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for alluvium_swarm-0.3.0.tar.gz
Algorithm Hash digest
SHA256 c89312a21b651a8264d2725cbc145824381a2775333404a7d59a45bd891cbd69
MD5 4770d51ed3c669fb095e9e6c16abe56c
BLAKE2b-256 e74df91817d4849f93c9213840d27ca3b1bf7090787acd59d787113a7f32d3c9

See more details on using hashes here.

File details

Details for the file alluvium_swarm-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: alluvium_swarm-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 27.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for alluvium_swarm-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 01ca894876a7e2567121f0a9162c58586feda7f0281462602bd2f2b59b74a719
MD5 b8a041e3ed51ebbd1bdf7ee654ede1e1
BLAKE2b-256 a28daa1d27de99cced924654a2cd848df25f1d363a1a7611e39bc9342f65923b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page