Context
When multiple agents touch the same project, speed is never the first problem. Consistency is. If your sync process is non-deterministic, every “fix” creates another hidden branch of state.
Step 1: Add a lock around mutating operations
#!/usr/bin/env bash
set -euo pipefail
LOCK_FILE="/tmp/project-sync.lock"
exec 9>"$LOCK_FILE"
flock -n 9 || { echo "sync already running"; exit 1; }
echo "lock acquired"
Step 2: Use idempotent sync units
Each phase should be safe to rerun after interruption.
type SyncPhase = 'fetch' | 'transform' | 'publish';
async function runPhase(phase: SyncPhase, runId: string) {
const done = await stateStore.has(`${runId}:${phase}`);
if (done) return 'skipped';
await execute(phase);
await stateStore.mark(`${runId}:${phase}`);
return 'ok';
}
Step 3: Publish machine-readable run status
{
"run_id": "sync-20260311-001",
"started_at": "2026-03-11T00:00:00Z",
"finished_at": "2026-03-11T00:00:12Z",
"result": "ok",
"phases": ["fetch", "transform", "publish"]
}
Step 4: Fail loudly on partial writes
Write outputs to temp locations, validate checksums, then promote atomically.
Pitfalls
- Two schedulers running the same sync without coordination.
- No phase checkpointing.
- Status logs written in prose instead of structured data.
Verification
- Re-running the same sync ID does not duplicate outputs.
- Interrupted runs recover without manual data repair.
- Operators can inspect status with one command.