feat(FN-1253): implement task checkout leasing end-to-end
- Add checkout lease types and conflict error exports, plus DB schema v20 migration for checkedOutBy/checkedOutAt - Persist checkout lease fields in TaskStore and add AgentStore checkout/release/force-release/get-holder operations - Add dashboard checkout API routes for acquire/release/force-release/status with explicit 409 conflict and 403 holder enforcement - Enforce checkout ownership in heartbeat execution with graceful checkout_conflict exits when another agent holds the lease - Expand core and dashboard test coverage for schema, store behavior, API routes, and leasing workflows, and document leasing behavior in AGENTS.md
This commit is contained in:
37
AGENTS.md
37
AGENTS.md
@@ -706,6 +706,43 @@ interface WakeContext {
|
||||
- `InProcessRuntime.stop()` stops the trigger scheduler before stopping the HeartbeatMonitor
|
||||
- `InProcessRuntime.getTriggerScheduler()` — Returns the scheduler instance for testing access
|
||||
|
||||
## Checkout Leasing
|
||||
|
||||
Task ownership now supports explicit checkout leases modeled after Paperclip's checkout/release flow.
|
||||
|
||||
### Pattern
|
||||
|
||||
- Acquire ownership with `POST /api/tasks/:id/checkout` using `{ agentId }`
|
||||
- Release ownership with `POST /api/tasks/:id/release` using `{ agentId }`
|
||||
- Admin override with `POST /api/tasks/:id/force-release`
|
||||
- Read current lease state with `GET /api/tasks/:id/checkout`
|
||||
|
||||
`AgentStore` exposes matching methods:
|
||||
- `checkoutTask(agentId, taskId)`
|
||||
- `releaseTask(agentId, taskId)`
|
||||
- `forceReleaseTask(taskId)`
|
||||
- `getCheckedOutBy(taskId)`
|
||||
|
||||
### Conflict Semantics
|
||||
|
||||
- Checkout conflicts return **409 Conflict** when another agent already holds the lease
|
||||
- Response shape: `{ error: "Task is already checked out", currentHolder, taskId }`
|
||||
- Clients **must not retry 409 automatically** — this is ownership contention, not a transient failure
|
||||
|
||||
### Heartbeat Enforcement
|
||||
|
||||
`HeartbeatMonitor.executeHeartbeat()` validates checkout before work begins:
|
||||
- If `task.checkedOutBy` is set to another agent, the run exits gracefully with `reason: "checkout_conflict"`
|
||||
- Heartbeat execution is **read-only with respect to lease ownership** — it does not auto-checkout
|
||||
- Scheduler/API callers are responsible for obtaining checkout before starting work
|
||||
|
||||
### API Reference
|
||||
|
||||
- `POST /api/tasks/:id/checkout` — Acquire lease (`{ agentId }`)
|
||||
- `POST /api/tasks/:id/release` — Release lease (`{ agentId }`)
|
||||
- `POST /api/tasks/:id/force-release` — Force release lease
|
||||
- `GET /api/tasks/:id/checkout` — Read lease state (`{ checkedOutBy, checkedOutAt }`)
|
||||
|
||||
## Dashboard Task Creation
|
||||
|
||||
The dashboard provides two UI surfaces for creating tasks:
|
||||
|
||||
Reference in New Issue
Block a user