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 NoneDecisions
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.