← patterns

Claims and locks

A claim is a small file that says who is working on what, taken before an agent session writes anywhere outside its own project. If the target already carries a fresh claim, the tool refuses the write. The pattern turns “please don’t touch this while I’m here” from a convention every session must remember into a mechanism none of them can argue with.

When it applies

  • More than one writer can reach the same tree or file: parallel agent sessions, scheduled jobs, a human with an editor open.
  • Sessions have a home tree but predictably reach past it: shared configs, memory files, “a quick fix in the sibling repo because the finding surfaced there”.
  • You have already seen one overwrite. Under context pressure, an agent treats another agent’s presence as noise, so the second collision is not a question of care.
  • It does not apply to a single session in a single repo with no shared surfaces; the ceremony buys nothing there and gets skipped.

The moving parts

One file per target, named after it, five fields:

{
  "target": "shared-config",
  "holder": "session-a41",
  "purpose": "align the lint rule with the new preset",
  "taken_at": "2026-08-14T21:02:11Z",
  "heartbeat_at": "2026-08-14T21:09:40Z"
}

Four verbs around it:

  1. take before the first foreign write, and read the file back.
  2. beat the heartbeat every writing turn, so a live claim is distinguishable from an abandoned one.
  3. release at the end; a machine at rest has an empty claims directory.
  4. refuse when the target is held and the heartbeat is fresh. REFUSED is an answer, not an obstacle: the refused session queues a handover note and the work happens later, in order.
foreign write wanted


 take <target> ── held, heartbeat fresh? ──yes──▶ REFUSED
      │ no, or stale                                 │
      ▼                                              ▼
 claim written, read back                    queue a handover,
      │                                      the work runs later

 beat every writing turn ──▶ release at the end

The part that pays for itself twice: verify the guard itself. A claim script that prints “claimed” while the write silently failed is worse than no guard, because sessions proceed holding a lock that does not exist. Slugified target names, a checked write, and a read-back close that hole. Test a safety mechanism for agents with the same suspicion you had for the agents.

What refusal looks like on screen when it works:

$ claim take shared-config "align the lint rule"
REFUSED: shared-config is claimed by session-a41 (heartbeat 41s ago).

The whole mechanism is about sixty lines of shell. The value is not the code; it is the moment the wrong move becomes loud instead of silent.

- A reminder is not a lock. Refusal has to be mechanical.

The incident that proved this pattern, five audited collisions and the guard that failed its own check, is its own piece.

carried by My agents kept overwriting each other, so I gave them claims