gludd

Autonomous Software Development

An AI agent that writes, tests, and ships code
— explained, with exact code paths

v0.1.0-beta.2 · MIT License · built 2026-07-14

What is gludd?

gludd is an AI agent that writes code for you.

You describe what you want built. gludd writes the code, tests it, reviews it, and saves it to your project — automatically.

Think of it like a junior developer that never sleeps.
You hand them a task. They write it, test it, have a senior dev check it, and commit it. Then they grab the next task.

Not a chatbot. Not a code-completion tool. A full agent that does the whole job — start to finish. This deck walks through the exact code paths and database tables behind every step.

The problem

Software development is slow and repetitive.

  • Writing boilerplate code takes hours
  • Writing tests for every change takes more hours
  • Reviewing code takes yet more hours
  • Fixing bugs found in review takes even more
  • And then you do it all again tomorrow

What if a machine could do all of that — while you focus on the hard problems?

That's what gludd does. You describe the goal. gludd handles the grind.

Architecture at a glance

daemon.py boots a FastAPI process and wires an EventLoop (event_loop/loop.py) that ticks forever.

  • Every tick opens one DB session and runs 16 named phases in a fixed order
  • A todo moves through a state machine: BACKLOG → SCHEDULED/QUEUED → ACTIVE → done/reconciled
  • Every state change, dispatch, review verdict, and commit is a database row — nothing lives only in memory
graph TD User["You"] -->|POST /api/todos| Daemon["daemon.py + EventLoop
event_loop/loop.py"] Daemon -->|dispatch| Model["Model gateway
models/gateway.py"] Daemon -->|execute| Runner["Ansible / worker
ansible/runner.py, worker/app.py"] Model --> Review["Reviewer
review/reviewer.py"] Runner --> Review Review -->|approve| Git[("git_automation/repo.py
commit + push")] Daemon -.->|next tick, ~1s| Daemon

Source: src/general_ludd/daemon.py, src/general_ludd/event_loop/loop.py

Daemon overview — the central orchestrator

src/general_ludd/daemon.py (3,126 lines) is the single entry point. It boots a FastAPI process, wires every subsystem, and runs the EventLoop forever.

graph TD subgraph Boot["Lifespan startup (daemon.py)"] A1["init DB + session factory
:1188"] --> A2["wire controllers
(SpendLimiter, BudgetGuard,
Hibernation, Pause)"] A2 --> A3["wire agents
(AgentDispatcher, ModelGateway,
AnsibleRunnerAdapter)"] A3 --> A4["register all routers
(todos, review, admin, MCP,
self-improve, compute...)"] A4 --> A5["start EventLoop
asyncio.create_task"] end subgraph Runtime["Per-tick (event_loop/loop.py)"] B1["open DB session"] --> B2["run 16 PHASE_ORDER phases"] B2 --> B3["commit + close session"] end subgraph Shutdown["Lifespan teardown"] C1["drain background tasks"] --> C2["close MCP subprocesses"] C2 --> C3["close DB connections"] end Boot --> Runtime Runtime -.->|"~1s tick"| Runtime Runtime --> Shutdown
  • Wired at boot: 24 model providers, 109 Ansible roles, 14 enforcement plugins, 27 DB tables
  • Routers registered: todos, review, admin, MCP, self-improve, compute, pause, remediation, eval, security, account, memory, worktree, stream, files, facts, ornith, benchmark, model-performance
  • Daemon state: single source of truth (<code>app.state</code>) — DB factory, controllers, agent registry, dispatcher, all in one process

Source: src/general_ludd/daemon.py:1-3126, src/general_ludd/event_loop/loop.py

The flagship flow: todo → AI implements → review → commit

Nine stages, each backed by a named function and a database table. The next slides walk each one.

flowchart TD n1["1 Submit
routers/todos.py"] --> n2["2 Tick loop
event_loop/loop.py"] n2 --> n3["3 Schedule
event_loop/scheduler.py"] n3 --> n4["4 Claim
db/repository.py"] n4 --> n5["5 Dispatch
loop.py _dispatch_execute_job"] n5 --> n6["6 Model call
models/gateway.py"] n6 --> n7["7 Execute
ansible/runner.py or worker/app.py"] n7 --> n8["8 Review
review/reviewer.py"] n8 --> n9["9 Reconcile + commit
git_automation/repo.py"] n8 -.->|reject: retry| n6

Source: code-trace audit, verified against src/general_ludd/ on 2026-07-12T10:39:48Z

The todo state machine

Every todo moves through one explicit lifecycle. Each arrow is a guarded transition (VALID_TRANSITIONS, schemas/todo.py:62) written to todo_events.

stateDiagram-v2 [*] --> BACKLOG BACKLOG --> SCHEDULED: has schedule BACKLOG --> QUEUED: ready now SCHEDULED --> QUEUED: due / cron fire QUEUED --> ACTIVE: claim CAS ACTIVE --> AWAITING_RESULT: dispatched AWAITING_RESULT --> REVIEWING_RETURN: result persisted REVIEWING_RETURN --> COMPLETE: approve REVIEWING_RETURN --> NEEDS_MORE_WORK: changes REVIEWING_RETURN --> FAILED: reject NEEDS_MORE_WORK --> QUEUED: retry ACTIVE --> BUDGET_EXCEEDED: spend gate ACTIVE --> BLOCKED_ON_HUMAN: needs approval COMPLETE --> [*]

Source: src/general_ludd/schemas/todo.py:12-106 (TodoStatus + VALID_TRANSITIONS)

Flagship flow · stage 1 of 9

1Submit

Everything starts with a todo — a short description of what you want done, submitted over HTTP.

flowchart TD A["POST /api/todos
routers/todos.py:170"] --> B["TodoRepository.create
db/repository.py"] B --> C["INSERT todos
status = BACKLOG"]
  • POST /api/todos is handled by api_add_todosrc/general_ludd/routers/todos.py:170
  • The router calls TodoRepository.createsrc/general_ludd/db/repository.py
  • One row lands in the todos table (db/models.py:180), status = BACKLOG
  • No todo_events row yet at this point — the first event is appended later, when the todo is claimed (see stage 4)

Like writing a sticky note and putting it on the board — except the board is a SQL table with a foreign key.

Flagship flow · stage 2 of 9

2The tick loop — 17 phases

daemon.py wires an EventLoop (event_loop/loop.py). Every tick runs PHASE_ORDER (loop.py:88) — the same 17 phases, in the same order, inside one DB session.

flowchart TD subgraph G1["Config + review intake"] A1["load_config_snapshot"] --> A2["claim_unreviewed_
task_returns"] A2 --> A3["dispatch_return_
review_jobs"] A3 --> A4["evaluate_pid_
controllers"] end subgraph G2["Scheduling + buckets"] B1["refill_task_buckets"] --> B2["run_scheduler"] B2 --> B3["claim_runnable_todos"] B3 --> B4["evaluate_rules"] end subgraph G3["Execution + reconcile"] C1["dispatch_execute_jobs"] --> C2["reconcile_completed_
decisions"] C2 --> C3["refresh_model_
performance"] end subgraph G4["Compute + credits"] D1["check_compute_
utilization"] --> D2["check_service_credits"] D2 --> D3["remediate_blocked_
tasks"] end subgraph G5["Self-improve + metrics"] E1["self_improve"] --> E2["poll_issue_sources"] E2 --> E3["emit_tick_metrics"] end G1 --> G2 G2 --> G3 G3 --> G4 G4 --> G5

Source: src/general_ludd/event_loop/loop.py:88-106 (PHASE_ORDER, 17 entries)

One tick, in order

The load-bearing phases of PHASE_ORDER, in the exact order they run inside a single tick's DB session.

flowchart TD A["load_config
snapshot"] --> B["claim + dispatch
return review"] B --> C["run_scheduler
promote due todos"] C --> D["claim_runnable
todos (CAS)"] D --> E["evaluate_rules"] E --> F["dispatch_execute
jobs (spend gate)"] F --> G["reconcile_completed
decisions + commit"] G --> H["self_improve"] H --> I["emit_tick_metrics"] I -.->|next tick ~1s| A

Source: src/general_ludd/event_loop/loop.py:88-106 (PHASE_ORDER)

Flagship flow · stage 3 of 9

3Schedule promotion

The run_scheduler phase calls TodoScheduler.tick()src/general_ludd/event_loop/scheduler.py:107 (class TodoScheduler at scheduler.py:88).

flowchart TD A["TodoScheduler.tick()"] --> B{"one-shot
or cron?"} B -->|one-shot, due| C["SCHEDULED -> QUEUED"] B -->|cron fire| D["spawn QUEUED child
advance next_run_at"]
  • A one-shot todo promotes SCHEDULEDQUEUED once now ≥ scheduled_at
  • A cron todo (todos.cron, croniter grammar) stays a SCHEDULED template forever — each fire spawns a QUEUED child clone and advances next_run_at
  • The due-todo lookup is index-backed: ix_todos_scheduled_lookup on (status, schedule_paused, next_run_at, scheduled_at)db/models.py:257

Source: src/general_ludd/event_loop/scheduler.py, src/general_ludd/db/models.py:227-269

Flagship flow · stage 4 of 9

4Claim — version-guarded CAS

gludd won't let two workers grab the same todo. The claim_runnable_todos phase (EventLoop._phase_claim_runnable_todos, loop.py:1365) calls a compare-and-swap.

flowchart TD A["claim_runnable_todos
phase"] --> B["TodoRepository.claim_runnable
CAS: QUEUED -> ACTIVE"] B --> C["acquire_lease()
bucket_leases row"] C --> D["todo_events row
actor=claim_runnable"]
  • db/repository.py:424 guards the UPDATE with the todos.version column; losing the race just leaves the row QUEUED for next tick
  • event_loop/lease.py:15 takes an exclusive lease on the todo's resource bucket
  • The lease is a real row in bucket_leases (db/models.py:495): bucket_key, holder_id, expires_at — a stale lease just expires
Flagship flow · stage 5 of 9

5Dispatch — the spend gate

_dispatch_execute_job (event_loop/loop.py:1844) runs from the dispatch_execute_jobs phase — but it checks the budget before it checks anything else.

flowchart TD A["_dispatch_execute_job"] --> B{"SpendLimiter.try_charge
(in-memory gate)"} B -->|accepted| C["proceed to model call"] B -->|rejected| D["defer todo
(retry later tick)"] C --> E["periodic flush ->
spend_records"]
  • SpendLimiter.try_charge (controllers/spend_limiter.py:278) checks a rolling-window budget before a single token is spent — an in-memory check-and-record, not a DB write
  • Accepted charges drain via a periodic flush (SpendRepository.add, db/repository.py:1773) into spend_records (db/models.py:633) — the in-memory ledger is the source of truth for the live gate, the table is the durable record
  • A rejected charge defers the todo silently: no model call, no cost incurred

The gate runs first. gludd cannot overspend by dispatching, then discovering it should not have.

Flagship flow · stage 6 of 9

6Model call — gateway + tool loop

flowchart TD A["invoke_model_for_generation"] --> B["ModelGateway.call_model
(LangChain + tenacity retries)"] B --> C["INSERT model_call_logs
+ model_performance rollup"] B --> D["ToolCallLoop.run_with_tools
(if tool use needed)"] D --> E["DynamicDispatcher
fails closed on unknown tool"]
  • models/job_invocation.py:41 calls models/gateway.py:642 — a LangChain provider registry with tenacity retries
  • Every call is billed and logged to model_call_logs (db/models.py:962) and rolled up into model_performance (db/models.py:1005)
  • Autonomous tool use hands off to execution/tool_loop.py:139: model proposes a tool call, gludd executes it, feeds the result back, repeats
  • Tool calls route through dispatch/dynamic_dispatcher.py:198 — a capability lattice that fails closed on any tool it doesn't recognize
Flagship flow · stage 7 of 9

7Execute — playbook or worker HTTP

Two execution paths converge on the same persistence call.

flowchart TD A["local: run_playbook
ansible/runner.py"] --> C["_persist_task_return
loop.py:3016"] B["remote: POST /jobs/execute
worker/app.py:315"] --> C C --> D["INSERT task_returns
(unreviewed)"]
  • Local: runner.write_vars (ansible/runner.py:140) + run_playbook (ansible/runner.py:166) hand the job to an Ansible playbook
  • Remote/isolated: POST /jobs/executeworker/app.py:315 — runs the same job over HTTP against a worker process
  • Either path's result flows into EventLoop._persist_task_returnevent_loop/loop.py:3016
  • One row lands in task_returns (db/models.py:306) — the raw, unreviewed result

Submit → execute, and what each step writes

flowchart TD s1["1 POST /api/todos
(You -> routers/todos.py)"] --> s2["2 INSERT todos
(BACKLOG)"] s2 --> s3["3 claim QUEUED -> ACTIVE
+ todo_events (EventLoop)"] s3 --> s4["4 SpendLimiter.try_charge
(gate)"] s4 --> s5["5 call_model
(models/gateway.py)"] s5 --> s6["6 INSERT model_call_logs"] s6 --> s7["7 execute job
(runner / worker)"] s7 --> s8["8 INSERT task_returns
(unreviewed)"]

Source: routers/todos.py api_add_todo, event_loop/loop.py (_dispatch_execute_job, _persist_task_return), models/gateway.py call_model

Flagship flow · stage 8 of 9

8Review — a second AI, adversarially

gludd doesn't let the same model that wrote the code approve it.

flowchart TD A["ReturnReviewer.review_return()"] --> B{"output parseable?"} B -->|no| C["decision = failed"] B -->|yes| D["decision = approve /
changes / reject"] C --> E["apply_decision()
task_decisions + audit_events"] D --> E
  • review/reviewer.py:49 runs an adversarial scan against the task_return, not just "did it run"
  • Model output is parsed JSON-fence tolerant (strips ```json fences) — an unparseable verdict becomes decision=failed, never a silent pass
  • review/decision_applier.py:28 writes the verdict
  • Results land in task_decisions (db/models.py:344, 1:1 with task_returns) and audit_events (db/models.py:413)

The review loop, in detail

flowchart TD A["Execute (stage 7)
job result"] --> B["INSERT task_returns
(loop.py:3016)"] B --> C["ReturnReviewer.review_return()
adversarial scan + JSON-fence-tolerant parse"] C --> D{"output parsed?"} D -->|no| E["decision = failed"] D -->|yes| F["decision = approve /
changes / reject"] E --> G["apply_decision()
INSERT task_decisions + audit_events"] F --> G G --> H["reconcile_completed_decisions
(loop.py:3066)"]

Source: src/general_ludd/review/reviewer.py:49, src/general_ludd/review/decision_applier.py:28

Flagship flow · stage 9 of 9

9Reconcile + commit

flowchart TD A["reconcile_completed_decisions
loop.py:3066"] --> B["_try_commit_completed_work
loop.py:3355"] B --> C["GitAutomation
commit + push (repo.py:180)"] C --> D["optional: PRDelivery
_maybe_open_pr"]
  • _phase_reconcile_completed_decisionsevent_loop/loop.py:3066 — reads the latest task_decisions and updates todo status
  • _try_commit_completed_workevent_loop/loop.py:3355 — the git-delivery path: the only point where a todo's affected files are actually known (worktree changes don't exist before this)
  • GitAutomationgit_automation/repo.py:180 — commits and pushes to a per-todo branch gludd-<todo_id>
  • Optional PR delivery via PRDeliverygit_automation/pr_delivery.py — used from EventLoop._maybe_open_pr (loop.py:3448)

Everything is traceable: what was asked, what was written, what was reviewed, what was committed — each step is a database row, not a log line.

Flagship flow · the whole path in one view

The complete code path

Every hop from HTTP submission to a git commit, with the file that implements it.

flowchart TD A["POST /api/todos
routers/todos.py:170"] --> B["todos table
db/models.py:180"] B --> C["EventLoop tick
PHASE_ORDER (event_loop/loop.py)"] C --> D["claim_runnable + bucket_leases
db/repository.py"] D --> E["dispatch_execute_jobs
event_loop/loop.py"] E --> F["two-phase model call
models/job_invocation.py + execution/tool_loop.py"] F --> G["task_returns
_persist_task_return (loop.py:3068)"] G --> H["review -> task_decisions
review/reviewer.py"] H --> I["reconcile + commit
loop.py:3066 -> git_automation/repo.py:180"]
  • Submitrouters/todos.py:170 inserts one todos row (db/models.py:180)
  • Claim — version-guarded CAS in db/repository.py (claim_runnable), leased via bucket_leases
  • Dispatchevent_loop/loop.py (dispatch_execute_jobs) builds the JobSpec and runs the model
  • Two-phase model call — tool-free generation (models/job_invocation.py via models/gateway.py), then the autonomous tool loop (execution/tool_loop.py)
  • Return & review — worker output lands in task_returns (loop.py:3068); a second model reviews it (review/reviewer.py) and writes task_decisions
  • Reconcile + commitloop.py:3066 applies the decision; git_automation/repo.py:180 commits + pushes

Source: code-trace audit against src/general_ludd/ on 2026-07-12T10:39:48Z

Flagship flow · subsystem map

Exact code paths — every subsystem, one slide

SubsystemKey file(s)What happens
HTTP APIrouters/todos.py:170POST /api/todos → INSERT todos (BACKLOG)
Event Loopevent_loop/loop.py:8816-phase tick, ~1s cycle, one DB session
Schedulerevent_loop/scheduler.py:107SCHEDULED → QUEUED promotion, cron fire
Claim (CAS)db/repository.py:424QUEUED → ACTIVE, version-guarded
Leaseevent_loop/lease.py:15bucket_leases row, holder_id + expires_at
Spend Gatecontrollers/spend_limiter.py:278try_charge (in-memory), defer on reject
Model Callmodels/gateway.py:642LangChain + tenacity retries, 24 providers
Tool Loopexecution/tool_loop.py:139Model proposes tool → execute → feed back
Executeansible/runner.py:166 / worker/app.py:315Local playbook or remote worker HTTP
Persistevent_loop/loop.py:3016INSERT task_returns (unreviewed)
Reviewreview/reviewer.py:49Adversarial scan, JSON-fence-tolerant parse
Decisionreview/decision_applier.py:28INSERT task_decisions + audit_events
Reconcileevent_loop/loop.py:3066Apply decision, update todo status
Commitgit_automation/repo.py:180git commit + push to gludd-<todo_id> branch
PR (optional)git_automation/pr_delivery.py_maybe_open_pr, via loop.py:3448

Source: code-trace audit against src/general_ludd/ on 2026-07-12T10:39:48Z

The database — 27 tables, zero drift

Every table used above (and 15 more) is defined once, in one file.

  • All 27 tables are declared in src/general_ludd/db/models.py
  • Migrations are a single linear chain: alembic/versions/ 001 → 024, no branches
  • ORM ↔ migration parity is enforced by a test, not a promise: tests/unit/test_alembic_orm_parity.py
flowchart TD DB[("27 tables
db/models.py")] --> WC["Work-cycle spine
(4 tables, next slide)"] DB --> SC["Scheduling + safety
+ cost (4 tables)"] DB --> MM["Models + memory
+ compute (5 tables)"]

Source: src/general_ludd/db/models.py (27 tables) · alembic/versions/001-024 · tests/unit/test_alembic_orm_parity.py

Key tables (1 of 3) — the work-cycle spine

TableLineWhat it stores
todos180One row per submitted task; state machine BACKLOG → ... → done
todo_events280Status-transition log per todo (FK todos.todo_id, CASCADE)
task_returns306Raw result of one dispatched job (stage 7 output, unreviewed)
task_decisions344Reviewer verdict for one task_return (1:1, FK CASCADE)
flowchart TD todos[("todos
180")] --> todo_events[("todo_events
280")] todos --> task_returns[("task_returns
306")] task_returns --> task_decisions[("task_decisions
344")]

Source: src/general_ludd/db/models.py

Key tables (2 of 3) — scheduling, safety, cost

TableLineWhat it stores
queues388Named queue config: caps, allowed playbooks/models, retry policy
audit_events413System-wide event log (decisions, leases, spend, more)
bucket_leases495Exclusive CAS lease per resource bucket (holder_id, expires_at)
spend_records633One row per spend event for the rolling-window cost limiter
flowchart TD todos[("todos")] --> queues[("queues
388")] todos --> bucket_leases[("bucket_leases
495")] todos --> spend_records[("spend_records
633")] task_decisions[("task_decisions")] --> audit_events[("audit_events
413")]

Source: src/general_ludd/db/models.py

Key tables (3 of 3) — models, memory, compute

TableLineWhat it stores
model_call_logs962Per-LLM-call record: tokens, latency, provider
model_performance1005Aggregated per-model / per-task-type performance stats
human_todos857Todos awaiting human approval or input
memory_records739Long-term agent memory entries
slurm_jobs1093HPC/Slurm job tracking for compute-heavy tasks
flowchart TD todos[("todos")] --> model_call_logs[("model_call_logs
962")] model_call_logs --> model_performance[("model_performance
1005")] todos --> human_todos[("human_todos
857")] todos --> memory_records[("memory_records
739")] todos --> slurm_jobs[("slurm_jobs
1093")]

Source: src/general_ludd/db/models.py

Behaviors → DB tables — what writes where

Every meaningful action in gludd writes a row to a specific table. Here is the complete mapping.

BehaviorTableLineWhat is recorded
Submit a tasktodos180Description, queue, priority, schedule, cost est.
Status transitiontodo_events289old_status → new_status, actor, reason
Claim a todobucket_leases504Exclusive CAS lease (holder_id, expires_at)
Spend gate passspend_records634Unix-ts cost event (rolling-window math)
Model API callmodel_call_logs962Tokens in/out, latency, provider, cost
Model perf rollupmodel_performance1005Aggregated success/latency/cost per model
Job executestask_returns315Raw result: exit code, summary, artifacts
Review verdicttask_decisions353approve / changes / reject, confidence
Any audit eventaudit_events422System-wide event log (all action types)
Needs humanhuman_todos857Tasks awaiting human approval or input
Agent memorymemory_records741Key-value store, TTL, per-agent namespace
Queue configqueues397Caps, allowed playbooks/models, retry policy
Role executionrole_runs665Accounting ledger per role invocation
Benchmark runbenchmark_results685Completion/quality/adherence/efficiency scores
Task embeddingtask_embeddings776Cosine-similarity routing vectors (RAG)

Source: src/general_ludd/db/models.py (27 total tables; 15 shown above are the behavioral spine)

MCP integration — the tool-call boundary

gludd implements the Model Context Protocol (MCP) as the boundary between AI reasoning and real-world execution. The model proposes a tool; MCP validates, executes, and returns the result.

graph TD subgraph MCP["src/general_ludd/mcp/"] A["client.py
MCPClient
stdin/stdout subprocess"] --> B["transport.py
Transport launcher
(python, node, uvx, npm)"] B --> C["loader.py
MCPToolLoader
register + load"] C --> D["registry.py
ToolRegistry
register + lookup"] D --> E["builtins.py
Built-in tools
(run_project_check...)"] C --> F["catalog.py
ToolCatalog
discover + list"] end subgraph Model["Model side"] M1["execution/tool_loop.py
model proposes tool call"] --> M2["DynamicDispatcher
capability lattice"] M2 --> M3["MCPClient.call_tool()"] M3 --> M4["return result to model"] end Model -.->|"validate + execute"| MCP
  • Transport: Python subprocess, Node subprocess, uvx, npm — all argv-validated against a per-interpreter allowlist
  • Secrets: mcp/secrets.py — MCP-specific secret resolution, scoped per server
  • Config: mcp/config.py — server discovery, allowlist, version pins
  • DynamicDispatcher fails closed on unknown tools — no tool the system doesn't recognize ever executes

Source: src/general_ludd/mcp/ (8 modules), src/general_ludd/execution/tool_loop.py:139, src/general_ludd/dispatch/dynamic_dispatcher.py

Self-improve pipeline — gludd audits itself

Every tick, the EventLoop runs a self-improve phase that audits gludd's own code, proposes fixes, gates them through approval, and tracks outcomes.

graph TD subgraph SI["src/general_ludd/self_improve/"] A["harness.py
SelfImproveHarness
gap-finding scan"] --> B["gate.py
SelfImproveGate
auto_queue + promote"] B --> C["approval.py
ApprovalController
human-in-the-loop gate"] C --> D["outcomes.py
OutcomeTracker
track success/failure"] end subgraph Loop["EventLoop phase (loop.py)"] E1["self_improve phase"] --> E2["Scan source tree
(lint, typecheck, audit)"] E2 --> E3["Propose fixes
(model-generated patches)"] E3 --> E4["Gate approval
(manual or auto)"] E4 --> E5["Apply approved fix
(hot-reload or commit)"] E5 --> E6["Track outcome
(test pass/fail, regressions)"] end Loop -.->|"reads + mutates"| SI
  • Harness: scans for gaps using SAST (semgrep, bandit), typecheck (mypy), lint (ruff)
  • Gate: auto_queue=True bypasses approval (P1 item C13 tracks this)
  • Approval: human-in-the-loop default; POST /admin/self-improve/run bypasses the gate
  • Apply path: currently hardcoded to make test-unit — generalization to external projects is D3 (P1)

Self-improve has 4 known gate bypasses (item C13). It works, but the approval path needs hardening.

Source: src/general_ludd/self_improve/ (4 modules), src/general_ludd/routers/self_improve.py, src/general_ludd/event_loop/loop.py self_improve phase

Security — built in, not bolted on

gludd has three layers of protection on every action:

  • Layer 1 Permissions — hard rules on what gludd is allowed to do
  • Layer 2 Runtime guards — 11 plugins that watch every action in real time
  • Layer 3 Prompt rules — the AI is instructed to follow safe practices
graph TD L1["Layer 1 - Permissions
hard rules in config files"] --> L2["Layer 2 - Runtime Guards
11 plugins watch every action"] L2 --> L3["Layer 3 - Prompt Rules
AI instructed to follow safe practices"]

Security findings: 14 of 15 fixed — one remains open and tracked.

13 BLOCKING enforcement plugins VERIFIED · 99 hook-runtime tests · 14/15 findings fixed · Source: .opencode/plugin/, docs/audit/

Guardrails architecture — 14 plugins, 3 layers

Every agent action passes through three enforceable layers. No single layer is trusted alone.

graph TD subgraph L1["Layer 1 — Config (hard gate)"] C1["opencode.json
permission block"] C2["Make only: deny *,
allow make *"] end subgraph L2["Layer 2 — Runtime (real-time hooks)"] P1["enforce-make.ts
Bash make-only"] P2["enforce-floor.ts
agent-count floor"] P3["enforce-stop.ts
premature-stop block"] P4["enforce-delegate.ts
delegation budget"] P5["enforce-no-wait.ts
anti-wait CI poll"] P6["enforce-session-start.ts
dispatch-on-start"] P7["enforce-verified-claims.ts
evidence-required"] P8["enforce-clean-tree.ts
dirty-tree dispatch deny"] P9["+5 more plugins
(13 BLOCKING total)"] end subgraph L3["Layer 3 — Prompt (instruction)"] D1["AGENTS.md
policy sections"] D2["TDD, commit-after-green,
guardrail integrity..."] end L1 --> L2 --> L3
  • 13 BLOCKING plugins in .opencode/plugin/, all hot-reload capable, zero advisory-only
  • 99 hook-runtime tests across 52 functional tests in 8 plugins (make test-hook-runtime)
  • Fail-open on error: a broken hook never blocks legitimate work; the next layer still enforces
  • All 13 verified active via make verify-plugin-manifest

Source: .opencode/plugin/ (13 BLOCKING), opencode.json, tests/unit/test_*_plugin*.py

The scale of the system

gludd is a substantial piece of software. Here's what's under the hood:

465+
source files
find src -name '*.py'
38,207
tests collected
make test-count
111
Ansible roles
make collection-roles
36
Ansible modules
collections/
27
DB tables
db/models.py
24
model providers
provider_presets.py
13
BLOCKING plugins
.opencode/plugin/
253
tasks at 99.6%
254 total, ~253 done
18
Terraform stacks
infra/terraform/
HTTP
Terraform backend
S3-compatible remote state
108
secrets e2e tests
tests/e2e/
SearX
model search
Terraform-provisioned

What gludd can do today (1 of 2)

Here's what the agent handles, right now:

  • Write code — new features, bug fixes, refactors
  • Write tests — unit tests, integration tests, end-to-end tests
  • Review code — a separate AI checks every change (stage 8)
  • Run quality gates — lint, typecheck, tests, security scans
  • Commit to git — with full audit trails (stage 9)

What gludd can do today (2 of 2)

  • Manage sprints — backlog grooming, sprint planning, retrospectives
  • Security audits — threat modeling, secret scanning, supply chain checks
  • Build complete apps — built 12 games from scratch, autonomously
  • Track costs — per-project, per-model cost accounting (spend_records)
  • Push notifications — Slack, webhook, and CLI alerts trigger when human action is needed
  • Provider smoke tests — low-cost API, model, compute, and multi-platform checks emit JSON evidence for repair agents
  • Self-improve — audits its own code and proposes improvements

253 tasks at 99.6% VERIFIED · Source: 254 total tasks, ~253 complete

What gludd can't do yet

Honest gaps — we won't oversell.

  • GAP No UI — gludd is command-line only, no graphical interface
  • GAP Single-worker — one task at a time, no parallel execution across machines
  • GAP SQLite only — no PostgreSQL, no horizontal scaling
  • PARTIAL CI not fully green — local gate passes, GitHub Actions still has issues
  • PARTIAL 1 security finding open — 14 of 15 fixed, one remains
  • PARTIAL 254 tasks at 99.6% — ~1 remaining task
  • PARTIAL No IDE integration — works in your terminal, not your editor

We'd rather tell you what's broken than pretend it's perfect. Every gap here is tracked and being worked on.

How to try it

Three steps to get started:

  1. Get the code
    git clone https://github.com/sandboxcom/gludd.git
    cd gludd
    make init
  2. Start the agent
    uv run gludd daemon --port 8000
  3. Submit your first task
    uv run gludd todo add "Write a unit test for the login endpoint"

That's it. gludd picks up the task and runs the nine-stage flagship flow shown earlier.

Full guide: README.md · Prerequisites: Python 3.11+, uv, an API key for one model provider

What's next (1 of 2)

Near term:

  • Green CI on GitHub Actions (close the local-vs-CI gap)
  • Fix the last security finding (14/15 → 15/15)
  • Complete Phase F (90% → 100%)
  • Live verification of all claimed features

Medium term:

  • Visual dashboard (TUI) — see tasks, costs, and progress live
  • Greenfield builds — "build me a todo app from scratch"
  • Multi-worker support — parallel task execution
  • PostgreSQL support — horizontal scaling

What's next (2 of 2)

Long term:

  • Fleet mode — multiple agents, shared work queue
  • Sandboxed execution — safe running of untrusted code
  • Deeper hardening — wider failure-mode coverage

Honest metrics

Every number below is from a machine-produced measurement.
No hardcoded counts — live tool output only.

v0.1.0-beta.2
current version
pyproject.toml
38,207
tests collected
make test-count
465+
source files
find src -name '*.py'
14/15
security findings fixed
docs/audit/
253
tasks at 99.6%
254 total
13 BLOCKING
enforcement plugins
make verify-plugin-manifest
18 stacks
Terraform infra
HTTP backend, SearX search
108
secrets e2e tests
tests/e2e/

These are truth, not aspiration. Every citation is a command you can run yourself. Updated 2026-07-14.

Links & resources

  • Source code
    github.com/sandboxcom/gludd
  • This presentation (live URL)
    sandboxcom.github.io/gludd — deployed by .github/workflows/pages.yml
  • This presentation (always-works fallback)
    docs/presentation/deck/index.html, open directly or via make deck-serve
  • Quick start guide
    See README.md in the repo
  • Code-path deep-dive
    The flagship-flow and schema slides of this deck: the 9-stage flow and 27-table schema
  • Security audit
    See docs/audit/ in the repo
  • Full feature list
    Run make gen-status-table after cloning
  • License
    MIT — free to use, modify, and distribute

Thank you

Questions?

gludd — Autonomous Software Development
v0.1.0-beta.2 · MIT License · built 2026-07-14

github.com/sandboxcom/gludd
sandboxcom.github.io/gludd

Every number in this deck comes from a command you can run yourself. Every code path was spot-checked against the tree it describes. Nothing here was hand-authored or fabricated.