Soft Claims vs Hard Leases in LoopX Todo Coordination: A Complete Technical Guide
Soft claims provide lightweight ownership hints for routing and visibility, while hard leases enforce exclusive write-scope with TTL protection—LoopX uses both mechanisms together to coordinate multi-agent access to todos.
LoopX implements a dual-layer coordination system for its todo-sharing protocol. Understanding the difference between soft claims and hard leases is essential for building robust multi-agent workflows in the huangruiteng/loopx repository. Both mechanisms serve distinct purposes in the shared-goal authority model: soft claims handle visibility, while hard leases guarantee exclusive mutation rights when needed.
What Are Soft Claims?
Soft claims represent the default coordination mechanism in LoopX. They function as non-exclusive ownership hints that allow agents to signal intent without blocking others.
How Soft Claims Work
A soft claim is stored directly in the todo's markdown content via the claimed_by field. As implemented in loopx/visible_governance.py, the system extracts these claims from the status queue to route tasks and display current ownership in ACTIVE_GOAL_STATE.md.
# Add a soft claim
loopx todo claim --todo-id 42 --agent-id alice
# Remove when finished
loopx todo unclaim --todo-id 42 --agent-id alice
The key characteristic: multiple agents can soft-claim the same todo simultaneously. This coexistence is possible because soft claims never interact with the lease store. The test in tests/control_plane/test_goal_handoff_mode.py (line 391) explicitly validates this behavior.
Soft Claim Properties
- No TTL: Claims persist as long as the todo remains open and the agent reports activity
- Crash recovery: Cleared automatically through standard todo-status updates if an agent fails
- Always visible: Appears in the shared canonical state regardless of lease configuration
What Are Hard Leases?
Hard leases provide optional, exclusive write-scope protection for scenarios requiring deterministic arbitration. Unlike soft claims, hard leases actively prevent concurrent mutation.
How Hard Leases Work
In loopx/control_plane/goals/shared_goal_alignment.py, hard leases are persisted as JSON files under goals/<goal>/task-leases/ and protected by file locks. Only one active lease exists per todo at any moment.
The protocol definition in docs/reference/protocols/host-integration-surface-v0.md (line 223) specifies four explicit operations:
# Acquire exclusive lease with 10-minute TTL
loopx task-lease acquire --todo-id 42 --ttl 600
# Renew before expiration
loopx task-lease renew --todo-id 42
# Explicit release
loopx task-lease release --todo-id 42
# Check status
loopx task-lease inspect --todo-id 42
Hard Lease Properties
- Optional TTL: Configurable expiration; see
examples/control_plane/todo-claim-lease-roadmap-smoke.py(line 55) for TTL handling - Single holder: Other agents see occupied status and must wait
- Crash safety: Stale lease detection via "inactive terminal records" prevents ABA issues during reacquisition (
shared_goal_alignment.py, line 284)
Programmatic Usage in LoopX SDK
Both mechanisms are accessible through the Python SDK:
from loopx import LoopXClient
client = LoopXClient()
# Soft claim — non-exclusive, always available
client.todo.claim(todo_id=42, agent_id="alice")
# Hard lease — exclusive, opt-in
lease = client.task_lease.acquire(todo_id=42, ttl=600)
# ... perform exclusive work ...
client.task_lease.release(lease_id=lease.id)
Side-by-Side Comparison
| Aspect | Soft Claim | Hard Lease |
|---|---|---|
| Scope | Visibility and routing | Exclusive mutation rights |
| Storage | Todo claimed_by markdown field |
goals/<goal>/task-leases/*.json with file lock |
| Concurrency | Multiple agents allowed | Single holder only |
| TTL | None | Optional, configurable |
| Management | Implicit claim/unclaim | Explicit acquire/renew/release/inspect |
| Visibility | Always in ACTIVE_GOAL_STATE.md |
Only when lease feature enabled |
| Failure handling | Cleared by status updates | Terminal record prevents ABA |
When to Use Each Mechanism
Use soft claims when:
- Agents need visibility into who intends to work on a task
- Cooperative coordination is sufficient
- You want minimal overhead and maximum flexibility
Use hard leases when:
- Exactly one agent must mutate the todo at a time
- Deterministic arbitration prevents race conditions
- TTL-based timeout protection is required for crash safety
Key Implementation Files
| File | Purpose |
|---|---|
loopx/visible_governance.py |
Soft claim extraction and routing logic |
loopx/control_plane/goals/shared_goal_alignment.py |
Hard lease validation, acquisition, and corruption handling |
docs/reference/protocols/host-integration-surface-v0.md |
CLI and API contract for hard leases |
tests/control_plane/test_goal_handoff_mode.py |
Validation that soft claims bypass the lease store |
examples/control_plane/todo-claim-lease-roadmap-smoke.py |
TTL handling demonstration |
Summary
- Soft claims are LoopX's default coordination mechanism—lightweight, non-exclusive ownership hints stored in todo markdown for routing and visibility
- Hard leases provide opt-in exclusive write-scope with TTL protection, implemented via file-locked JSON in
goals/<goal>/task-leases/ - Both mechanisms coexist: soft claims never read the lease store, allowing parallel claims while hard leases enforce single-writer semantics when explicitly requested
- Choose soft claims for cooperative workflows; add hard leases when deterministic arbitration and crash-safe timeouts are required
Frequently Asked Questions
Can an agent hold both a soft claim and hard lease on the same todo?
Yes. These mechanisms are independent. An agent typically maintains its soft claim for visibility in ACTIVE_GOAL_STATE.md while holding a hard lease for exclusive mutation rights. The soft claim signals intent; the hard lease enforces it.
What happens if a hard lease expires while work is in progress?
The lease becomes invalid. Agents must proactively renew leases before TTL expiration using loopx task-lease renew. The recommendation in todo-claim-lease-roadmap-smoke.py is to set TTLs generously and implement heartbeat renewal patterns for long-running work.
Why does LoopX retain "inactive terminal records" for expired leases?
To prevent ABA problems. As implemented in shared_goal_alignment.py (line 284), a crashed agent's stale lease could be silently replaced if the system reused lease identifiers. Terminal records preserve a tombstone history, ensuring that reacquisition attempts expose the intervening expiration clearly.
Are hard leases required for basic LoopX operation?
No. Hard leases are entirely optional. The system functions fully with soft claims alone. Hard leases activate only when an agent explicitly calls loopx task-lease acquire or the equivalent SDK method, making them opt-in infrastructure for contention-heavy scenarios.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →