Concept 7 of 10
Run a second agent on the same repo without the two clobbering each other. Point five agents at one repo and they overwrite each other's edits and corrupt the tree. Flowgate makes the collision impossible — the runtime owns the shared state and hands out only non-overlapping work.
Workflow state lives in a shared store the runtime owns — the coordination state about the repo, sitting alongside the working tree rather than inside any one agent's head. Each agent receives only the slice relevant to its current step. The shared truth is in one place, which is what makes running more than one agent on the same repo tractable at all. (For where this state lives and how you run it, see how you run flowgate.)
Agent B won't silently overwrite agent A's fix. Every read returns the
workflow version, and a write must echo it back as
expectedVersion. If another agent moved the state in between,
the write is rejected with STALE_WORKFLOW_VERSION and the
fresh version comes back — so the agent re-reads and retries instead of
clobbering work it never saw. Optimistic concurrency, enforced by the
runtime.
→ flowgate.command { "transition": "edit", "expectedVersion": 7, ... }
← { "error": { "code": "STALE_WORKFLOW_VERSION",
"message": "expected version 7, current is 9" },
"workflow": { "version": 9 } } // re-read, then retry
A transition declares the files it will write with owned_files.
Before it runs, the runtime takes a global lock on them — across
every workflow running against the repo; the repo is the protected unit —
and releases it when the step finishes. If another agent already holds one
of those files, this one doesn't fail and doesn't stomp: it
durably suspends with status waiting_on_lock
and auto-resumes the moment the files free, in FIFO order —
even across a restart. A dead holder's lock is reaped on a TTL, so nothing
deadlocks.
editing_auth:
transitions:
apply_patch:
target: verifying
executor:
kind: agent
owned_files: ["src/auth.rs", "src/session.rs"] # locked globally for this step
A second agent reaching for src/auth.rs while it's held doesn't
get an error — it gets parked and woken when the file is free:
← { "result": { "status": "waiting_on_lock" },
"context": { "_lock_wait": { "files": ["src/auth.rs"] } } }
# … the holder finishes, the file frees, and this workflow auto-resumes (audit: lock.resumed)
Once the work is partitioned into non-overlapping pieces, fan it out. The
parallel executor runs N branches concurrently and aggregates
them under a join condition — all, any, or
at_least: K.
executor:
kind: parallel
branches:
- { kind: workflow, definitionId: review-security }
- { kind: workflow, definitionId: review-perf }
- { kind: workflow, definitionId: review-style }
join: { at_least: 2 }
max_concurrency: 4