Why

I use several coding agents every day. Running more than one at a time by hand means losing track of which session is doing what, which branch it is on, and whether its work has been reviewed. Alfred makes that state explicit.

Shape

Alfred is a Python CLI. Domain logic sits in the middle and knows nothing about the outside world. Everything it touches, the agent tools, Git, tmux and the filesystem, is an adapter behind an interface, and all of them are wired together in one place at startup.

The agents are driven through custom MCP servers and tool adapters, so Alfred talks to each of them through the same small set of operations.

State machines

Tasks and agent sessions each have their own finite state machine. Transitions are written as an explicit table instead of being scattered through if statements, so an illegal move is an error at the point it is attempted, not a strange state discovered later.

# Simplified. The real tables have more states.
TASK_TRANSITIONS = {
    ("queued",    "start"):   "running",
    ("running",   "submit"):  "in_review",
    ("running",   "fail"):    "failed",
    ("in_review", "approve"): "done",
    ("in_review", "reject"):  "running",
}

def transition(state: str, event: str) -> str:
    try:
        return TASK_TRANSITIONS[(state, event)]
    except KeyError:
        raise IllegalTransition(state, event) from None

Decisions

D1An append-only event log

Every transition is recorded as an event and never edited afterwards. When a session goes wrong, the history of what happened is still there to read.

D2Roll back when the tracker sync fails

Alfred mirrors task state to an external tracker. If that sync fails, the local transition is rolled back, so the two never disagree about where a task is.

D3Plain JSON for state

State lives in JSON files. It is easy to inspect, easy to diff, and enough for one person's workload. A database would be the first change if this ever ran for a team.

← All projects