Skip to main content

Zero Merge Conflicts with Pre-Edit Hooks

The #1 pain point in multi-agent development: merge conflicts. Two agents edit the same file. Git detects conflicts. You spend 15 minutes manually resolving. Repeat 12-20 times per week. ULPI’s pre-edit hook solves this permanently. Before any file edit, the hook checks reservations and blocks conflicts before they happen. Result: Zero merge conflicts. Guaranteed.

The Problem: Uncoordinated File Access

Without Pre-Edit Hooks

  • Timeline: The Conflict
  • The Cost
10:00 AM - Alice (Cursor) starts editing auth.ts
// Alice's changes
export async function login(credentials) {
  // Adding OAuth support
  const token = await oauth.authenticate(credentials);
  return token;
}
10:15 AM - Bob (Claude Code) also starts editing auth.ts (doesn’t know Alice is working on it)
// Bob's changes
export async function login(credentials) {
  // Adding rate limiting
  await rateLimiter.check(credentials.email);
  const token = await authenticateUser(credentials);
  return token;
}
10:30 AM - Alice commits and pushes10:45 AM - Bob tries to push…
$ git push
! [rejected] main -> main (fetch first)
error: failed to push some refs

$ git pull
Auto-merging src/auth.ts
CONFLICT (content): Merge conflict in src/auth.ts
10:45 AM - 11:00 AM - Bob spends 15 minutes manually resolving conflictTotal time wasted: 15 minutes × 12 conflicts/week = 3 hours/week lost to merge conflicts

The Solution: Pre-Edit Hook + File Reservations

How It Works

1

Agent Attempts Edit

Alice (Cursor) wants to edit src/auth.ts:
// Alice's intention
await editFile('src/auth.ts', newContent);
2

Pre-Edit Hook Intercepts

BEFORE the edit executes, the pre-tool-use:edit hook fires:
🎣 Pre-Edit Hook Triggered

File: src/auth.ts
Agent: Claude-Cursor-Alice
Action: Edit

Checking file reservations...
3

Check Existing Reservations

Hook queries ULPI Coordination API:
# MCP Tool: list-file-reservations
GET /api/coordination/file-reservations?path=src/auth.ts
Response:
{
  "reservations": []  // No existing reservations
}
4

Create Reservation

File is available, so hook creates reservation:
# MCP Tool: reserve-file-paths
POST /api/coordination/file-reservations
{
  "paths": ["src/auth.ts"],
  "mode": "shared",  // or "exclusive"
  "duration": 7200,   // 2 hours
  "agent_id": "claude-cursor-alice"
}
Response:
{
  "status": "success",
  "reservation_id": "res_abc123",
  "expires_at": "2024-01-15T12:30:00Z"
}
5

Allow Edit

Hook returns exit code 0 → Edit proceeds:
✓ File Reserved

File: src/auth.ts
Mode: Shared
Expires: 2 hours
Reservation ID: res_abc123

Edit allowed. Proceeding...
Alice successfully edits the file.
6

Bob Tries to Edit Same File

15 minutes later, Bob (Claude Code) attempts to edit auth.ts:
await editFile('src/auth.ts', bobsChanges);
Pre-edit hook fires again…
7

Detect Conflict

Hook checks reservations:
GET /api/coordination/file-reservations?path=src/auth.ts
Response:
{
  "reservations": [{
    "file": "src/auth.ts",
    "agent_id": "claude-cursor-alice",
    "agent_name": "Alice",
    "mode": "shared",
    "expires_at": "2024-01-15T12:30:00Z"
  }]
}
File is already reserved!
8

Block Edit

Hook returns exit code 2Edit blocked:
⛔ Edit Blocked - File Reserved

File: src/auth.ts
Reserved by: Alice (Claude-Cursor-Alice)
Mode: Shared
Expires: 1 hour 45 minutes

This file is currently being edited by another agent.

Coordination Options:
1. [Message Alice] - Coordinate changes
2. [Wait for release] - Set reminder
3. [Edit anyway] - Request override (admin only)
4. [Work on different file] - Avoid conflict

[Choose Option]
Bob cannot edit the file. Conflict prevented.
9

Coordinate Instead of Conflict

Bob chooses option 1 (Message Alice):
📨 Send Message to Alice

Subject: auth.ts changes
Message: Hey Alice, I see you're working on auth.ts. I need to add
rate limiting. Want to coordinate so we don't conflict?

[Send] [Cancel]
Alice receives message and they coordinate:
Alice: "I'm adding OAuth. Should be done in 30 min. Can you wait?"
Bob: "Perfect, I'll work on the rate limiter middleware separately,
      then you can call it from auth.ts when you're done."
Result: Coordination instead of conflict

Reservation Modes

Pre-edit hooks support two reservation modes:

Shared Mode (Default)

Use for: Config files, documentation, non-critical edits Behavior:
  • Multiple agents can reserve the same file in shared mode
  • Edits are allowed concurrently
  • Agents are notified of other shared reservations
  • Coordination encouraged but not enforced
Example:
✓ File Reserved (Shared Mode)

File: README.md
Your reservation: Shared
Other agents with shared reservations:
- Alice (expires in 45m)
- Charlie (expires in 1h 20m)

⚠️  Reminder: Coordinate with other agents to avoid overwriting changes.

Edit allowed.
When to use:
  • Documentation files
  • Configuration files (if small changes)
  • Comment-only edits
  • Files with clear sections (different agents edit different sections)

Exclusive Mode

Use for: Critical code files, database migrations, breaking changes Behavior:
  • Only one agent can hold exclusive reservation
  • All other agents blocked from editing
  • Strictest conflict prevention
Example:
✓ File Reserved (Exclusive Mode)

File: src/database/migrations/001_add_users.ts
Mode: Exclusive
Expires: 2 hours

No other agents can edit this file while you have exclusive lock.

Edit allowed.
When to use:
  • Database migration files
  • Configuration files with complex interdependencies
  • Critical business logic files
  • Files undergoing major refactors

Reservation Management

Auto-Reservation on First Edit

By default, pre-edit hooks automatically create reservations:
~/.ulpi/config.json
{
  "hooks": {
    "preEdit": {
      "autoReserve": true,        // Auto-create reservation on first edit
      "defaultMode": "shared"      // Use shared mode by default
    }
  }
}
Behavior:
  1. First edit of a file → Reservation created automatically
  2. Subsequent edits → Existing reservation reused (if still valid)
  3. Reservation expires → New reservation created on next edit

Manual Reservations

You can also reserve files manually before editing:
# Reserve a single file
ulpi coordination reserve-file src/auth.ts --mode exclusive --duration 4h

# Reserve multiple files
ulpi coordination reserve-files \
  src/auth.ts \
  src/middleware/auth.ts \
  src/types/auth.types.ts \
  --mode shared \
  --duration 2h

# Reserve entire directory
ulpi coordination reserve-directory src/auth/ --mode exclusive
When to use manual reservations:
  • You know you’ll be working on multiple related files
  • Major refactor affecting many files
  • Want to claim files before starting work
  • Prevent others from editing during planning phase

Viewing Reservations

See all active file reservations:
# List all reservations in current project
ulpi coordination list-reservations

# Filter by agent
ulpi coordination list-reservations --agent alice

# Filter by mode
ulpi coordination list-reservations --mode exclusive
Example output:
📋 Active File Reservations

Your Reservations:
├─ src/auth.ts (shared, expires in 1h 30m)
├─ src/utils.ts (shared, expires in 45m)
└─ src/api.routes.ts (shared, expires in 2h)

Other Agents:
├─ src/database/schema.ts (Alice, exclusive, expires in 3h)
├─ src/middleware/cors.ts (Bob, shared, expires in 1h)
└─ README.md (Charlie, shared, expires in 20m)

Total: 6 active reservations

Releasing Reservations

Reservations are automatically released when:
  • Session ends (session-end hook)
  • Reservation expires (default 2 hours)
  • File is committed (optional - configure via releaseOnCommit)
Manual release:
# Release specific file
ulpi coordination release-file src/auth.ts

# Release all your reservations
ulpi coordination release-all

# Release expired reservations (cleanup)
ulpi coordination cleanup-reservations

Conflict Resolution Strategies

When pre-edit hook blocks your edit, you have options:

Strategy 1: Coordinate via Messaging

Best for: Normal collaboration, non-urgent edits
⛔ Edit Blocked

[Message Alice]

→ Opens messaging interface:

📨 New Message to Alice

Subject: Coordinate on auth.ts changes

Template suggestions:
- "When will you be done with auth.ts?"
- "Can we split the work? I'll handle X, you handle Y?"
- "I have urgent changes. Can you pause and let me edit?"

[Choose Template] [Write Custom]
Outcome: Coordinate and avoid conflict

Strategy 2: Wait for Expiration

Best for: Non-urgent edits, when waiting is acceptable
⛔ Edit Blocked

File: src/auth.ts
Reserved by: Alice
Expires in: 1 hour 45 minutes

[Wait for expiration]

→ Set reminder:

⏰ Reminder Set

I'll notify you when src/auth.ts is available.
Estimated: 1 hour 45 minutes

You can work on other files while waiting.

[OK]
Outcome: Wait, then edit when file becomes available

Strategy 3: Edit Different File

Best for: When you can make progress elsewhere
⛔ Edit Blocked

[Work on different file]

→ Suggests alternative files:

💡 Suggestions

You can work on these related files instead:
- src/middleware/auth.ts (available)
- src/types/auth.types.ts (available)
- tests/auth.test.ts (available)

[Select File]
Outcome: Make progress on related work

Strategy 4: Request Override (Emergency)

Best for: Critical bugs, security issues, production emergencies
⛔ Edit Blocked

[Request emergency override]

→ Override request form:

🚨 Emergency Override Request

File: src/auth.ts
Current holder: Alice
Reason: [Critical security fix - SQL injection vulnerability]

Override will:
- Force-release Alice's reservation
- Notify Alice of override
- Log event in audit trail
- Require admin approval (if configured)

⚠️  Use only for emergencies. Requires justification.

[Request Override] [Cancel]
Outcome: Admin approves → You get access (Alice’s reservation released)
Emergency overrides should be rare. They bypass the conflict prevention system. Use only for true emergencies (security issues, production outages, etc.).

Real-World Scenarios

Scenario 1: Parallel Feature Development

Setup:
  • Alice working on OAuth integration
  • Bob working on rate limiting
  • Both features touch auth.ts
Without Hooks:
  • Both edit auth.ts simultaneously
  • Merge conflict when Bob pulls Alice’s changes
  • 15 minutes lost to conflict resolution
  • Risk of broken merge
With Pre-Edit Hooks:
  • Alice edits auth.ts → Auto-reserved (shared mode)
  • Bob tries to edit → Blocked with Alice’s reservation shown
  • Bob messages Alice: “I need to add rate limiting”
  • Alice: “I’m adding OAuth. How about you create middleware/rateLimiter.ts and I’ll call it from auth.ts?”
  • Bob creates separate middleware file
  • Alice finishes OAuth, calls Bob’s middleware
  • Zero conflicts, better architecture

Scenario 2: Major Refactor

Setup:
  • Charlie refactoring entire authentication module
  • Touches 15 files across src/auth/
Without Hooks:
  • Charlie edits files one by one
  • Alice and Bob don’t know about refactor
  • They make changes to auth files
  • Massive merge conflicts when Charlie is done
With Pre-Edit Hooks:
  • Charlie reserves entire src/auth/ directory (exclusive mode)
  • All 15 files locked to Charlie
  • Alice tries to edit auth/login.ts → Blocked
  • Message shown: “Charlie is refactoring the auth module (estimated 3 hours)”
  • Alice and Bob work on other areas
  • Charlie completes refactor, releases reservations
  • Zero conflicts, team aware of major work

Scenario 3: Database Migration

Setup:
  • Alice creating new database migration
  • Bob trying to create another migration simultaneously
Without Hooks:
  • Both create migrations with same timestamp/number
  • Migration system breaks (duplicate versions)
  • Manual intervention required
With Pre-Edit Hooks:
  • Migrations directory configured with exclusive mode
  • Alice creates migration → Directory reserved (exclusive)
  • Bob tries to create migration → Blocked
  • Bob sees: “Alice is creating a migration (estimated 10 minutes)”
  • Bob waits, creates his migration after Alice
  • Migrations created in correct order, no conflicts

Performance & Latency

Hook Execution Time

Pre-edit hook is the fastest hook (~120ms average):
  • Latency Breakdown
  • User Experience
  • Optimization
Total: ~120ms
  1. Hook trigger: ~5ms (local)
  2. Check reservations API call: ~60ms (network)
  3. Decision logic: ~5ms (local)
  4. Create reservation (if needed): ~50ms (network)
  5. Return to IDE: ~0ms
Network calls: 1-2 (check reservations, optionally create)

Configuration

Customize Pre-Edit Behavior

~/.ulpi/config.json
{
  "hooks": {
    "preEdit": {
      // Enable/disable the hook
      "enabled": true,

      // Automatically create reservations on first edit
      "autoReserve": true,

      // Default reservation mode ("shared" or "exclusive")
      "defaultMode": "shared",

      // Reservation duration in seconds (default 2 hours)
      "reservationDuration": 7200,

      // Block conflicting edits (true = strict, false = warn only)
      "blockConflicts": true,

      // Show coordination guidance when blocked
      "showGuidance": true,

      // Allow emergency overrides
      "allowOverrides": true,

      // Require admin approval for overrides
      "requireAdminApproval": false,

      // Release reservation on file commit
      "releaseOnCommit": false,

      // File patterns for exclusive mode (glob patterns)
      "exclusivePatterns": [
        "**/migrations/**",
        "**/schema.ts",
        "**/*.sql"
      ],

      // File patterns to ignore (never reserve)
      "ignorePatterns": [
        "**/*.md",
        "**/*.txt",
        "**/package-lock.json"
      ]
    }
  }
}

Per-Project Overrides

Create .ulpi/config.json in project root:
.ulpi/config.json
{
  "hooks": {
    "preEdit": {
      // More strict for this critical project
      "defaultMode": "exclusive",
      "blockConflicts": true,
      "allowOverrides": false
    }
  }
}
Project config overrides global config.

Success Metrics

Teams using pre-edit hooks report:

Zero Merge Conflicts

Down from 12-20 per week to 0 conflicts100% reduction in merge conflict time wasted

3 Hours Saved Weekly

Per developer, per week$15,000 annual savings per developer

Better Architecture

Forced coordination leads to better design decisionsFewer rushed merges, more thoughtful collaboration

Team Awareness

Everyone knows what others are working onReduced duplicate work and conflicts

Best Practices

Recommendation: Start with shared mode for most filesRationale:
  • Allows multiple agents to work on same file (if coordinating)
  • Less restrictive than exclusive
  • Teams coordinate naturally when they see shared reservations
Switch to exclusive only when:
  • Database migrations
  • Complex refactors
  • Critical business logic
Recommendation: Reserve all related files before starting major refactorExample:
# Before starting OAuth refactor
ulpi coordination reserve-files \
  src/auth/*.ts \
  src/middleware/auth.ts \
  src/types/auth.types.ts \
  --mode exclusive \
  --duration 4h
Benefit: Prevents others from starting conflicting work
Recommendation: Match expiration to expected work duration
  • Quick fixes: 1 hour
  • Feature work: 2-4 hours
  • Major refactors: 8 hours (full workday)
Avoid: 24+ hour reservations (locks files overnight)
Recommendation: Don’t wait for expiration if you finish early
# Manual release when done
ulpi coordination release-all
Benefit: Files available sooner for teammates
Recommendation: When blocked, message the agent with reservationGood message examples:
  • “When will you be done with auth.ts? I have urgent fix.”
  • “Can we split the work? You handle OAuth, I’ll do rate limiting?”
  • “I see you’re refactoring. Need any help?”
Avoid: Radio silence or force overrides

Troubleshooting

Pre-edit hook not blocking conflicts

Symptoms: You can edit files reserved by others Solutions:
  1. Check preEdit.blockConflicts is true in config
  2. Verify Coordination subscription is active
  3. Ensure you’re connected: ulpi coordination status
  4. Check reservation exists: ulpi coordination list-reservations
  5. Enable debug logging: ulpi config set debug=true

Edit blocked but file shows as available

Symptoms: Hook says file is reserved, but list-reservations shows it as available Solutions:
  1. Stale cache - Wait 10 seconds and try again
  2. Reservation expired between check and edit
  3. Other agent released reservation just now
  4. Refresh reservation list: ulpi coordination list-reservations --refresh

Can’t override emergency situation

Symptoms: Override request denied even though it’s critical Solutions:
  1. Check allowOverrides is true in config
  2. If admin approval required, contact admin
  3. Use CLI override with justification: ulpi coordination override src/auth.ts --reason "Production down, SQL injection fix"
  4. As last resort, disable hook temporarily (NOT RECOMMENDED)

Next Steps