// FIELD NOTE
My AI Orchestrator Is Dumb on Purpose

Cory LaNou

My AI Orchestrator Is Dumb on Purpose
Overview
Most people assume the AI system running my backlog must be smart. It isn't, and I built it dumb on purpose. Detent moves real work from a backlog to a merged pull request without anyone babysitting it, but the orchestrator itself knows nothing about software. Every bit of intelligence lives in files I wrote, in plain text, for each project. In the video I open the actual files from my production projects — no mockups — and this post is the deep-dive version, with the real config excerpts inline.
Two layers, split on purpose
Detent is a single Go binary that treats a status board as a state machine. An issue moves from To Do, to In Progress, to review, to merged, and that status is the source of truth. The configuration splits into two layers. One global config runs the host, and one workflow file defines each project. The global config decides how much runs at once. The workflow file decides what good engineering looks like for that repo.
That split is deliberate. The orchestrator stays a dumb traffic cop, and all the judgment lives in files you own, version, and review like code.
The global config — the real file
This is my actual global config. It fits on one screen, and that's the point.
# ~/.config/detent/global.yaml — the whole host, one screen
global:
max_concurrent_agents: 5
scheduling: weighted
fair_share:
half_life: 1h
startup:
jitter_seconds: 10
max_spawn_per_second: 2
It caps the whole machine at five concurrent agents, no matter how many projects are asking for work. Scheduling is weighted, with a fair-share half life of one hour, so one busy project can't starve the others. Startup gets ten seconds of jitter and a cap of two spawns per second, because five agents slamming GitHub at the same instant is how you eat your rate limit.
Then comes the project list. Each entry is just an ID, a path to the workflow file, a working directory, a weight, and a priority. Client work is ranked ahead of my own site, so paying projects get dispatched first when everything wants attention.
projects:
- id: digitaldrywood
workflow: /home/corylanou/projects/digitaldrywood/digitaldrywood/WORKFLOW.md
workdir: /home/corylanou/projects/digitaldrywood/digitaldrywood
weight: 1
priority: 3
- id: creswoodcorners-phone
workflow: /home/corylanou/projects/digitaldrywood/creswoodcorners-phone/WORKFLOW.md
workdir: /home/corylanou/projects/digitaldrywood/creswoodcorners-phone
weight: 1
priority: 2
- id: digitaldrywood-video
workflow: /home/corylanou/Dropbox/Digital Drywood/Video Production/WORKFLOW.md
workdir: /home/corylanou/Dropbox/Digital Drywood/Video Production
weight: 1
priority: 2
Notice what's missing. There is nothing in this file about tests, reviews, branches, or how to write software. The host schedules work, and that is all it does.
One file, two halves
Every project points at one workflow file, and that file has two halves. The top half is machine config — the tracker binding, the board states, the workspace rules, the agent limits, and the validation stages. Below that sits a prose contract: the prompt every agent receives, written in plain English, describing exactly how work happens in this repo. The config wires the system to your board, and the prose teaches the agent your engineering process. The whole thing is one file, checked into the repo and reviewed like any other change.
The tracker and the states
A project can bind to more than one kind of board. Detent speaks Linear, GitHub Projects, plain repository labels, or no board at all in its own native mode, where all the state stays local on SQLite with no API limits — that's how the pipeline behind the video itself runs. My site runs in label mode, where plain repository labels carry the status, so the board never leaves GitHub.
# WORKFLOW.md — the tracker binding: plain GitHub labels carry the status
tracker:
kind: github
github_status_source: label
repository: digitaldrywood/digitaldrywood
status_label_prefix: "detent:"
active_states:
- Todo
- In Progress
- Rework
- Merging
observed_states:
- Backlog
- Human Review
- Blocked
terminal_states:
- Done
- Cancelled
The states split into three groups, and that grouping is the safety model. Active states are where agents actually run. Observed states are watched but never dispatched. Terminal states end the story. An agent physically cannot pick up work from Backlog, because Backlog is not an active state.
Dependencies are handled in config too. If an issue declares that it's blocked by another issue, Detent parks it and watches the blocker. The moment the blocker merges, the issue moves itself back to To Do. Nobody wakes up at night to unblock a queue.
# blocked issues watch their blocker and release themselves
dependency_auto_unblock:
enabled: true
source_states:
- Blocked
target_state: Todo
readiness: terminal_or_merged
Worktrees and hooks
The workspace section is where isolation comes from. Every issue gets its own git worktree, on its own branch, created from my source checkout. Five agents can work in parallel because they never share a directory. Nothing can step on the main checkout, and nothing can step on a sibling. Idle worktrees clean themselves up after a day, and a sweep runs every ten minutes to enforce it.
# every issue gets its own git worktree, on its own branch
workspace:
root: /home/corylanou/code/digitaldrywood-detent-workspaces
source_root: /home/corylanou/projects/digitaldrywood/digitaldrywood
auto_branch: true
cleanup_idle_ttl_ms: 86400000 # idle worktrees expire after a day
cleanup_sweep_interval_ms: 600000 # sweep every ten minutes
Hooks exist so every fresh worktree comes up with a valid environment. When a worktree is created, my after-create hook copies the environment file over, allows it with direnv, and installs node modules if they're missing.
hooks:
timeout_ms: 600000
after_create: |
SOURCE_REPO=/home/corylanou/projects/digitaldrywood/digitaldrywood
git -C "$SOURCE_REPO" fetch origin main -q 2>/dev/null || true
git -C "$SOURCE_REPO" worktree prune >/dev/null 2>&1 || true
if [ -f "$SOURCE_REPO/.envrc" ] && [ ! -f .envrc ]; then
cp "$SOURCE_REPO/.envrc" .envrc
fi
if command -v direnv >/dev/null 2>&1 && [ -f .envrc ]; then
direnv allow . >/dev/null 2>&1 || true
fi
if [ -f package-lock.json ] && [ ! -d node_modules ]; then
npm ci
fi
That's project knowledge encoded where it belongs. Detent has no idea what my environment variables are, and it shouldn't. It just runs my hook.
Agent limits and the merge train
The agent section sets the local limits — five concurrent agents, every run capped at twenty turns. One line in this section carries more weight than the rest: the Merging state is capped at exactly one agent. That single line is the merge train. Only one pull request rebases, watches CI, and merges at a time, so parallel branches never invalidate each other's green build.
agent:
max_concurrent_agents: 5
max_turns: 20
max_concurrent_agents_by_state:
Merging: 1 # <- the merge train. One PR rebases and merges at a time.
dispatch_priority_by_state:
- Merging
- Rework
- In Progress
- Todo
dispatch_priority_by_label:
- priority
- next-up
- enhancement
- content-ready
auto_promote:
enabled: true
quiet_seconds: 0
optout_label: requires-human-review
Dispatch priority is explicit — Merging beats Rework, Rework beats In Progress, and labels I chose can jump an issue up the queue. Auto-promotion is on, and there's an opt-out: any issue labeled requires-human-review will sit and wait for me, because some changes should never merge themselves.
The gates are your stages
I'd rather describe a gate as a stage a senior engineer already refuses to skip. You reproduce the problem before touching code, you plan the change, you write it with tests, you get it reviewed, and you prove it end to end. A gate is just a stage that must pass before the work advances — the same process you already believe in, enforced every single time.
gate:
kind: command
run: make check # full build, generated templates, linters, tests
require_automated_review: false
ci_failure_action: rework # red CI on the PR head -> straight to Rework
For my site, the validation command is make check, and that runs the full build, the generated templates, the linters, and the test suite. If CI fails on the pull request head, the config moves the issue to Rework instead of letting it rot. An agent picks it back up, reads the feedback, and fixes it.
The quiet period is a dial. My site sets it to zero and promotes immediately, but you can make every pull request soak until activity settles before it advances. You can go further: there's an optional validator agent that reviews the pull request and blocks promotion below a score, and there's a mode that waits for a human approval label. The gate is a policy you choose, not a default you inherit.
The prose contract
Under the config sits the contract, and this is the half that does the heavy lifting. It opens by templating in the issue — the title, the current state, the labels, and the full description — so the agent starts every run knowing exactly what it's working on. Then it lays down authority: the agent must read the project's own instruction files first, and when rules conflict, the stricter rule wins.
It demands a workpad — one persistent issue comment that keeps the plan, the acceptance criteria, the validation evidence, and the handoff notes together:
Keep a single persistent GitHub issue comment headed `## Codex Workpad`. Use it
for the plan, acceptance criteria, validation evidence, blockers, and handoff
notes. Do not scatter progress across multiple comments.
The workpad also keeps a machine-readable status block. The agent sets it to in progress, blocked, or complete, and Detent reads the block — never the prose.
The contract walks every issue through the stages themselves:
5. Reproduce or confirm the requested behavior before changing code when the
issue is a bug or behavior change.
6. Implement the smallest complete change.
7. Run focused validation and tests required by `AGENTS.md`, `CLAUDE.md`, and
the issue.
8. Run `make check` from the repo root.
9. Run pre-commit checks and confirm they pass. Treat any failing hook as
blocking; never bypass hooks to create the commit.
10. Commit and push a branch.
11. Open or update a GitHub PR with concrete summary and validation details.
It even handles scope creep. If the agent finds real work outside the issue, the contract says to open a new issue in Backlog rather than expanding this one. None of that is Detent being clever. Those sentences came out of my head, from years of doing this the hard way, and now they run on every issue in parallel.
Same binary, different cultures
The customization really shows up when you put two projects side by side. My site validates with make check. Creswood Corners, a client project, validates with the test suite plus a full Go build, because that repo defines done differently. My site runs the agent with the sandbox wide open, because it's my machine and I accept that trade. The client project locks it down to workspace writes. On my site, red CI moves the issue straight to Rework. On the client project it just sits and waits, because I look at those failures myself.
# digitaldrywood (my site): my machine, my risk tolerance
codex:
thread_sandbox: danger-full-access
gate:
kind: command
run: make check
ci_failure_action: rework # red CI goes straight back to an agent
# creswoodcorners (client work): locked down, failures wait for me
codex:
thread_sandbox: workspace-write
gate:
kind: command
run: make test && go build ./...
ci_failure_action: skip # red CI sits until a human looks
Even the dispatch politics differ — the client board moves bug and operations labels to the front, while my site gives content work the fast lane. It's the same binary and the same board shape, running two completely different engineering cultures. The system didn't decide any of that. The workflow files did.
Backlog to Done, no babysitting
Watch how the whole thing moves. An issue starts in Backlog, where it's visible but untouchable. I write the spec — the scope, the acceptance criteria, and the tests I expect — and I move the card to To Do. That is my entire involvement in starting the work.
Detent claims it, cuts a worktree, runs my bootstrap hook, and hands the agent the contract with the issue templated in. The agent works the stages, opens a pull request with the validation evidence attached, and sets its workpad status to complete. Checks go green on the head commit, the issue promotes to Merging, and the train takes over one candidate at a time. The rebase lands, CI passes on the final head, the pull request merges, and the issue moves to Done.
I was not sitting there for any of it. I reviewed a spec at the start and a pull request at the end, which are the two places human judgment actually belongs.
Why dumb wins
Anything smart the runtime did on its own would be a decision I never made and can't version. Instead, the intelligence stays in my spec and the runtime supplies the discipline. Every rule I believe in runs on every issue, every time, without me repeating myself.
That is the difference between this and vibe coding. Nothing merges here on vibes. It merges because the stages I defined passed, with the evidence written down. When a better model comes out next year, I swap the model. The process stays, because the process was never in the model to begin with.
You don't have to write any of this
You are not required to know any of this to get started. Detent's setup is built to be run by your agent. It follows the setup docs, it asks you how you work, and it takes the best path through. Maybe that's a GitHub Projects board. Maybe it's plain repository labels like mine. Or maybe it's no tracker at all, with Detent running fully local on its own SQLite database and giving you the same Kanban board feel. Every option in this post gets written for you, and you tune it from there. The files are the contract, but the setup is a conversation.
If you want to read these files yourself, Detent's own orchestration config is public — it dispatches the agents that build Detent itself. And I'd like to know where your workflow file would differ from mine: which stage would you never let an agent skip? Drop it in the comments on the video.
The repo and the docs are on GitHub. Point your agent at it and let it onboard itself.


