Separate Artifact Ownership Before Running in Parallel
The most reliable way to avoid conflicts is to not let independent workers write to one object. Each work item declares a write set; the control plane compares the actual outputs with the declaration. For files this is paths or globs, for a database entity keys, for documents sections, for external systems specific resource ids and operations.
readonly #items = new Map<string, Artifact>();
readonly #contracts: ReadonlyMap<AgentId, AgentContract>;
public constructor(contracts: readonly AgentContract[]) {
this.#contracts = new Map(contracts.map((contract) => [contract.id, contract]));
}
public write(writer: AgentId, draft: ArtifactDraft): Artifact {
const contract = this.#contracts.get(writer);
if (!contract) {
throw new Error(`Unknown agent ${writer}`);
}
const allowed = contract.allowedWritePrefixes.some((prefix) => draft.key.startsWith(prefix));
if (!allowed) {
throw new Error(`Agent ${writer} cannot write ${draft.key}`);
}
const current = this.#items.get(draft.key);
const currentVersion = current?.version ?? 0;
if (currentVersion !== draft.expectedVersion) {
throw new ArtifactConflictError(
`Version conflict for ${draft.key}: expected ${draft.expectedVersion}, actual ${currentVersion}`
);
}
const artifact: Artifact = {
...draft,
version: currentVersion + 1,
writer
};
this.#items.set(draft.key, artifact);
return artifact;
}
public read(key: string): Artifact | undefined {
return this.#items.get(key);
}
public list(): readonly Artifact[] {
return [...this.#items.values()].sort((left, right) => left.key.localeCompare(right.key));
}
}The write strategy depends on how shared the object is - and the choice should be made before the run.
Strategy · When to apply · What to check
- Single writer - A critical shared artifact; All workers return proposals, the writer applies them
- Disjoint partitions - Independent files or records; A non-overlapping write set before the run
- Optimistic concurrency - Rare conflicts; expectedVersion and retry with a fresh read
- Deterministic reducer - Map/reduce outputs; Schema, dedupe key, and a stable merge order
- Human merge - Semantically conflicting decisions; Explicit alternatives and evidence
A Git worktree doesn't solve everything. Different worktrees isolate the filesystem checkout, but two agents can still change one logical contract in different ways. There's no merge conflict if the lines differ, yet the architectural conflict already exists.
In the companion project the tester writes only to evidence/tests/, the security worker only to evidence/security/, the docs worker only to release-notes/. The reviewer reads these artifacts and owns report/. The limits are enforced by the store, not by a request in the prompt.
Ownership is separated. Now the parallel run: schedule a ready set, not a queue of conversations.