Pathfinder learned to run more than one agent at a time
My workflow kit was built to keep a human in the loop, one ticket at a time. Now it can coordinate several isolated agents at once, without becoming an autopilot.
For months, Pathfinder did one thing very deliberately: it made me the bottleneck.
Load a ticket. Start it. Ask for a review. Accept the result. Merge it. One at a time.
That was never an accident. The kit exists because a coding agent can execute far faster than it can be trusted, and the gap between those two speeds is exactly where bad software gets written.
So I put a human in every place where a decision had consequences.
It worked. It also got slow for a reason that had nothing to do with judgment.
The waiting that wasn't mine
Most of the queue wasn't waiting on me thinking.
It was waiting on me being available.
Two tickets that touch completely different parts of a codebase, with no dependency between them, would still run one after the other, because there was exactly one session and I was in it.
That's not human-in-the-loop. That's human-as-a-scheduler.
So Pathfinder now has two execution modes over the same ticket lifecycle.
One marker line
The mode lives in a tracked file, context/execution-mode.md, and the part the tool actually reads is one line:
<!-- pathfinder:execution-mode orchestrator -->
There are two valid values, human-in-the-loop and orchestrator. A missing file means human-in-the-loop, so every project installed before modes existed needs no migration at all.
A file with an invalid marker doesn't get guessed at. The ticket lifecycle says so once and proceeds human-in-the-loop. Orchestration refuses to run.
You choose it at install, or switch later:
npx create-pathfinder --mode orchestrator
What a worker actually gets
A claim is not a label. It's a Git fact.
Claiming a ticket creates a linked worktree at .pathfinder/worktrees/<key> and its own ticket/<key>-<slug> branch. What stops two workers owning the same ticket is Git's own claim ref and worktree checks, not a lock file I invented.
Each worktree carries its own context/current-ticket.md, so one worker's state can never overwrite another's. That single detail is the reason parallel work stops being terrifying.
The plan you approve, once
start plans before it claims.
A ticket is eligible only when its status is Proposed or Ready and every blocker is Complete.
Before anything is claimed, you see each selected ticket, its estimate, role, model, effort, and proposed worker assignment. Then you approve once.
That approval covers execution, claims, ticket-branch commits and pushes, draft PRs, and tracker writes.
It does not cover merge.
The worker limit defaults to three.
Estimates are evidence, not promises
Every dispatch exposes the same shape:
estimate(ticket, in-flight claims) → execution profile
select(profile, policy) → role, model, effort
dispatch(brief) → worker session
The profile records complexity, context size, parallel safety and risk, with the reason for each and whether it was derived or assessed. Parallel safety reports isolated, shared-surface or serialize.
I'm careful about how much weight that carries. Those estimates come from the paths a ticket declares, so they are coarse evidence that two branches probably won't collide, not proof. Actual changed-file overlap gets checked again at integration.
This release ships static routing: developer implements, tester reviews, and the model and reasoning effort are inherited from the dispatching session. Dynamic model routing is not in it. I'd rather ship the boring version of a routing layer and learn what it needs.
Harness translation covers claude-code, codex and manual. A value the harness can't honor is refused out loud rather than quietly downgraded.
Gates are the whole point
When a worker hits something that needs a person, it records State: human-gate and the exact question.
Its dependents wait. Everything unrelated keeps going.
On GitHub Issues the orchestrator adds a gate: human label and one idempotent comment, so a retry can't spam the issue.
Execution state (ready, working, review, human-gate, done, stale, failed, integrated) is a different vocabulary from ticket lifecycle (Proposed → Ready → In Progress → Complete). Conflating them is how a tool starts quietly deciding things.
Integration is a separate decision
Work complete is not the same as safe to merge.
The integrator takes reviewed, pushed work and checks it against the current default branch: changed-file overlap, divergence, git merge-tree conflict evidence, review, CI. It returns one of three answers.
candidate: present the merge under your approval policy.
behind: resume the existing developer to update onto the default branch and re-verify.
conflict: serialize the conflicting work and hand it back to its developer.
Because a merge changes the evidence, every remaining branch gets checked again afterward. A behind branch is never dressed up as a clean candidate.
It survived its own acceptance run
The part I actually care about isn't the design. It's that the feature was built by the feature, and the run went wrong in a useful way.
Pathfinder selected two tickets to run concurrently while a third stayed correctly blocked. It created distinct claims, branches and worktrees, and dispatched Codex workers.
Then both worker sessions hit usage limits and ended.
The work survived. The branch, worktree, claim, state file and execution profile are all durable, so replacement sessions resumed the same claims and carried on.
One ticket integrated first. The orchestrator reported the second as behind, dispatched a rebase-and-reverify brief against its preserved worktree, and only after fresh review and integration did the real dependency graph make the third ticket eligible.
I didn't plan that failure. It's the best evidence in the whole Feature.
A quota limit, a crash or a terminated harness does not erase a ticket:
/orchestrate resume 53.4
A compatible harness can even replace the worker without replacing the ticket. A Claude Code session on an inherited profile can be resumed by Codex in the same worktree and branch. There's no automatic failover, and I don't want one. Two sessions mutating one worktree is the failure mode worth being paranoid about.
What it is not
No daemon. No broker. No queue. No database. No server.
No automatic merging, no bypassing review, no unlimited agents.
The engine reads the ticket store, the blockers, Git branches and each worker's state file every time it runs. There is nothing to start and nothing to keep alive. It needs Node and Git, plus gh if your tickets live in GitHub Issues, while the human-in-the-loop workflow still needs no runtime at all.
Where it sits right now
Nine tickets, nine squash-merged pull requests, and one acceptance run with its snapshots and transcripts recorded rather than summarized. Every suite green: orchestration 166/166, installer 693/693, renderer 825 passing with 25 documented skips.
No version bump, no npm publication, no tag, no release. The capability sits under [Unreleased] until I decide it's worth shipping, which is its own decision and not the same as finishing the work.
Pathfinder can now run several agents at once. It still can't approve, accept, merge or release anything.
Pathfinder on GitHub: https://github.com/rikilamadrid/pathfinder
🧭 The kit owns the workflow. The project owns the stack.