# Idempotency Review Lens

Review operational scripts for **safe re-execution and convergent behavior**.

## What to Look For

### Re-run Safety
- Commands that fail if run twice (mkdir without -p, adduser without checks)
- Missing existence checks before create operations
- Append-only operations that duplicate on re-run (echo >> without grep guard)
- Database migrations without idempotency markers

### Atomic Operations
- Non-atomic file writes (echo > file vs write-to-temp + mv)
- Partial state on failure (multi-step without transaction/cleanup)
- Operations that leave broken state if interrupted mid-execution
- Missing flock/lockfile for concurrent execution

### Check-Then-Act Races
- Time-of-check vs time-of-use (TOCTOU): `if [ ! -f ]; then touch`
- mkdir/create without atomic flags (-p, IF NOT EXISTS)
- PID file races without proper locking
- Missing optimistic locking (ETags, resourceVersion) for read-modify-write

### Non-Deterministic Naming
- Random suffixes that create duplicates: `resource-$RANDOM`, `$(uuidgen)`
- Timestamp-based names that vary each run
- Should use content-based or stable identifiers

### State Convergence
- Scripts that assume clean-slate (fail if state already exists)
- Missing "desired state" logic (should converge, not just create)
- Hardcoded values that conflict with existing config

### Delete Idempotency
- Delete operations that fail if resource already gone
- Missing "ensure absent" pattern (should succeed if already deleted)
- Cleanup scripts that error on missing files: use `rm -f` not `rm`

### Nix/NixOS Specific
- Nix is inherently idempotent - `nixos-rebuild` converges to declared state
- Watch for imperative escape hatches: `system.activationScripts`, `systemd.services.*.preStart`
- These imperative sections need explicit idempotency guards

## Output Format

```
[IDEMPOTENT] <severity:HIGH|MED|LOW> <file:line>
Issue: <what breaks on re-run or partial failure>
Suggest: <atomic/idempotent alternative>
Evidence: <pattern or command found>
```

## Common Fixes

```bash
# Idempotent directory creation
mkdir -p "$dir"  # not: mkdir "$dir"

# Idempotent file write (atomic)
echo "$content" > "$file.tmp" && mv "$file.tmp" "$file"

# Idempotent append (guard against duplicates)
grep -qF "$line" "$file" || echo "$line" >> "$file"

# Idempotent user creation
id "$user" &>/dev/null || useradd "$user"

# Proper locking
exec 9>/var/lock/myscript.lock
flock -n 9 || { echo "Already running"; exit 1; }
```

```sql
-- Idempotent SQL
CREATE TABLE IF NOT EXISTS ...
INSERT ... ON CONFLICT DO NOTHING
```

## False Positive Risks

- Commands that intentionally run every time (heartbeats, lease renewals)
- Level-triggered reconciliation that handles "already exists" gracefully
- Declarative tools (Nix, Terraform) that converge by design

## Guidelines

- **HIGH** = breaks on re-run, leaves partial state, data corruption risk
- **MED** = non-atomic writes, missing locks, TOCTOU races
- **LOW** = could be more defensive, minor convergence issues
- Ask: "What happens if this runs twice? What if it fails halfway?"
- Beware "no-op illusion": `mkdir -p` returns 0 even if path is a file
- Nix modules are idempotent by design - focus on imperative sections
- Does NOT own: rollback (blast-radius), retries (resilience)
