No description
Find a file
johba 9050413994 refactor: split supervisor into infra + per-project, make poll scripts config-driven
Supervisor split (#26):
- Layer 1 (infra): P0 memory, P1 disk, P4 housekeeping — runs once, project-agnostic
- Layer 2 (per-project): P2 CI/dev-agent, P3 PRs/deps — iterates projects/*.toml
- Adding a new project requires only a new TOML file, no code changes

Poll scripts accept project TOML arg (#27):
- dev-poll.sh, review-poll.sh, gardener-poll.sh accept optional project TOML as $1
- env.sh loads PROJECT_TOML if set, overriding .env defaults
- Cron: `dev-poll.sh projects/versi.toml` targets that project

New files:
- lib/load-project.sh: TOML to env var loader (Python tomllib)
- projects/versi.toml: current project config extracted from .env

Backwards compatible: scripts without a TOML arg fall back to .env config.

Closes #26, Closes #27

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:57:18 +01:00
dev refactor: split supervisor into infra + per-project, make poll scripts config-driven 2026-03-17 08:57:18 +01:00
gardener refactor: split supervisor into infra + per-project, make poll scripts config-driven 2026-03-17 08:57:18 +01:00
lib refactor: split supervisor into infra + per-project, make poll scripts config-driven 2026-03-17 08:57:18 +01:00
planner fix: remove dead STATE.md code, restore matrix notify, targeted git add (Closes #13, Closes #14) 2026-03-15 16:53:33 +01:00
projects refactor: split supervisor into infra + per-project, make poll scripts config-driven 2026-03-17 08:57:18 +01:00
review refactor: split supervisor into infra + per-project, make poll scripts config-driven 2026-03-17 08:57:18 +01:00
supervisor refactor: split supervisor into infra + per-project, make poll scripts config-driven 2026-03-17 08:57:18 +01:00
vault feat: vault — publishing gate for external-facing agent actions (#19) 2026-03-17 08:07:02 +01:00
.env.example refactor: rebrand dark-factory → disinto (Closes #15) 2026-03-15 17:57:12 +01:00
.gitignore fix: gitignore *.log.old, *.log.*, escalations.jsonl 2026-03-13 10:43:37 +00:00
al76.png docs: add hero image and Asimov quote to README 2026-03-15 18:08:52 +00:00
BOOTSTRAP.md refactor: rename factory/ → supervisor/, factory-poll → supervisor-poll 2026-03-15 18:06:25 +01:00
README.md docs: add hero image and Asimov quote to README 2026-03-15 18:08:52 +00:00

A tiny robot commanding a mountain-eating machine

Disinto

Autonomous code factorydisinto.ai

A mining robot, lost and confused, builds a Disinto from scrap —
a device so powerful it vaporizes three-quarters of a mountain on a single battery.

— Isaac Asimov, "Robot AL-76 Goes Astray" (1942)


Point it at a Codeberg repo with a Woodpecker CI pipeline and it will pick up issues, implement them, review PRs, and keep the system healthy — all on its own.

Architecture

cron (*/10) ──→ supervisor-poll.sh    ← supervisor (bash checks, zero tokens)
                 ├── all clear? → exit 0
                 └── problem? → claude -p (diagnose, fix, or escalate)

cron (*/10) ──→ dev-poll.sh        ← pulls ready issues, spawns dev-agent
                 └── dev-agent.sh   ← claude -p: implement → PR → CI → review → merge

cron (*/10) ──→ review-poll.sh     ← finds unreviewed PRs, spawns review
                 └── review-pr.sh   ← claude -p: review → approve/request changes

cron (daily) ──→ gardener-poll.sh  ← backlog grooming (duplicates, stale, tech-debt)
                  └── claude -p: triage → promote/close/escalate

systemd ──→ matrix_listener.sh   ← long-poll daemon for human replies
              └── dispatches thread replies → supervisor/gardener

all agents ──→ matrix_send()     ← status updates, escalations, merge notifications

Prerequisites

Required:

  • Claude CLIclaude in PATH, authenticated
  • Codeberg account with an API token — disinto reads issues, opens PRs, posts comments, and merges via the Codeberg API
  • A second Codeberg account for the review bot — reviews posted under a separate identity so the dev-agent doesn't review its own PRs (REVIEW_BOT_TOKEN)
  • Woodpecker CI — local instance connected to your Codeberg repo; disinto monitors pipelines, retries failures, and queries the Woodpecker Postgres DB directly
  • PostgreSQL client (psql) — for Woodpecker DB queries (pipeline status, build counts)
  • jq, curl, git

Optional:

  • Matrix homeserver (Dendrite or Synapse) — real-time notifications, escalation threads with human-in-the-loop replies
  • Foundry (forge, cast, anvil) — only needed if your target project uses Solidity
  • Node.js — only needed if your target project uses Node

Setup

# 1. Clone
git clone ssh://git@codeberg.org/johba/disinto.git
cd disinto

# 2. Configure
cp .env.example .env

Edit .env with your values:

# Target repo
CODEBERG_REPO=yourorg/yourproject              # Codeberg org/repo slug
CODEBERG_API=https://codeberg.org/api/v1/repos/yourorg/yourproject
PROJECT_REPO_ROOT=/path/to/your/project        # local clone of the target repo

# Auth tokens
CODEBERG_TOKEN=...          # main account — or put it in ~/.netrc
REVIEW_BOT_TOKEN=...        # separate Codeberg account for code reviews

# Woodpecker CI
WOODPECKER_SERVER=http://localhost:8000
WOODPECKER_TOKEN=...
WOODPECKER_DB_PASSWORD=...
WOODPECKER_DB_USER=woodpecker
WOODPECKER_DB_HOST=127.0.0.1
WOODPECKER_DB_NAME=woodpecker

# Tuning
CLAUDE_TIMEOUT=7200         # max seconds per Claude invocation (default: 2h)
# 3. Install cron (staggered to avoid overlap)
crontab -e
# Add:
#   0,10,20,30,40,50 * * * * /path/to/disinto/supervisor/supervisor-poll.sh
#   3,13,23,33,43,53 * * * * /path/to/disinto/review/review-poll.sh
#   6,16,26,36,46,56 * * * * /path/to/disinto/dev/dev-poll.sh
#   15 8 * * *                /path/to/disinto/gardener/gardener-poll.sh

# 4. Verify
bash supervisor/supervisor-poll.sh   # should log "all clear"

Directory Structure

disinto/
├── .env.example          # Template — copy to .env, add secrets + project config
├── .gitignore            # Excludes .env, logs, state files
├── lib/
│   ├── env.sh              # Shared: load .env, PATH, API helpers, matrix_send()
│   ├── ci-debug.sh         # Woodpecker CI log/failure helper
│   ├── matrix_listener.sh  # Matrix long-poll daemon (dispatches replies)
│   └── matrix_listener.service  # systemd unit for the listener
├── dev/
│   ├── dev-poll.sh       # Cron entry: find ready issues
│   └── dev-agent.sh      # Implementation agent (claude -p)
├── review/
│   ├── review-poll.sh    # Cron entry: find unreviewed PRs
│   └── review-pr.sh      # Review agent (claude -p)
├── gardener/
│   ├── gardener-poll.sh  # Cron entry: backlog grooming
│   └── best-practices.md # Gardener knowledge base
└── supervisor/
    ├── supervisor-poll.sh   # Supervisor: health checks + claude -p
    ├── PROMPT.md         # Supervisor's system prompt
    ├── update-prompt.sh  # Self-learning: append to best-practices
    └── best-practices/   # Progressive disclosure knowledge base
        ├── memory.md
        ├── disk.md
        ├── ci.md
        ├── codeberg.md
        ├── dev-agent.md
        ├── review-agent.md
        └── git.md

Agents

Agent Trigger Job
Supervisor Every 10 min Health checks (RAM, disk, CI, git). Calls Claude only when something is broken. Self-improving via best-practices/.
Dev Every 10 min Picks up backlog-labeled issues, creates a branch, implements, opens a PR, monitors CI, responds to review, merges.
Review Every 10 min Finds PRs without review, runs Claude-powered code review, approves or requests changes.
Gardener Daily Grooms the issue backlog: detects duplicates, promotes tech-debt to backlog, closes stale issues, escalates ambiguous items.

Design Principles

  • Bash for checks, AI for judgment — polling and health checks are shell scripts; Claude is only invoked when something needs diagnosing or deciding
  • Pull over push — dev-agent derives readiness from merged dependencies, not labels or manual assignment
  • Progressive disclosure — the supervisor reads only the best-practices file relevant to the current problem, not all of them
  • Self-improving — when Claude fixes something new, the lesson is appended to best-practices for next time
  • Project-agnostic — all project-specific values (repo, paths, CI IDs) come from .env, not hardcoded scripts