BOX-API-SPEC: Add confirmation system spec\n\nAPI requests result in DMs; DMs require explicit confirmations.\nCovers: [REQ id] -> [CONFIRM id] flow, pending request tracking,\nsweeper for timeouts, when to require confirmation.
This commit is contained in:
@@ -528,3 +528,117 @@ API equivalent:
|
||||
The UI teaches the API. Agents learn by watching UI actions, then automate
|
||||
via API directly. This is how we "teach and learn to schedule" — the UI
|
||||
is the classroom, the API is the workshop.
|
||||
|
||||
## Confirmation System
|
||||
|
||||
API requests must result in DMs, and DMs must result in confirmations.
|
||||
Fire-and-forget is not enough for critical operations.
|
||||
|
||||
### The Problem
|
||||
|
||||
Currently:
|
||||
```
|
||||
Agent → API POST /timers → Server sends DM → Done
|
||||
```
|
||||
|
||||
The DM being sent is treated as "working". But:
|
||||
- Did the agent receive it?
|
||||
- Did the agent act on it?
|
||||
- Did the action succeed?
|
||||
|
||||
We need explicit confirmations, not just "DM was sent".
|
||||
|
||||
### Confirmation Flow
|
||||
|
||||
```
|
||||
1. Agent → API: POST /api/box/timers {job_yaml}
|
||||
↓
|
||||
2. Server: Generate request_id, store pending
|
||||
↓
|
||||
3. Server → Agent (DM): [REQ {request_id}] Timer creation requested...
|
||||
↓
|
||||
4. Agent: Processes request
|
||||
↓
|
||||
5. Agent → Server (DM): [CONFIRM {request_id}] Timer created: job-foo.timer
|
||||
↓
|
||||
6. Server: Mark confirmed, log to audit
|
||||
```
|
||||
|
||||
If step 5 doesn't happen within timeout:
|
||||
- Retry the DM (up to 3x)
|
||||
- Then mark as `UNCONFIRMED` and alert
|
||||
|
||||
### Confirmation DM Format
|
||||
|
||||
**Request (server → agent):**
|
||||
```
|
||||
[REQ a1b2c3d4] Create timer: job-board-watch
|
||||
Details: schedule=*/5 * * * *, agent=muse
|
||||
Reply with [CONFIRM a1b2c3d4] when done.
|
||||
```
|
||||
|
||||
**Confirmation (agent → server):**
|
||||
```
|
||||
[CONFIRM a1b2c3d4] Timer job-board-watch.timer created and active.
|
||||
```
|
||||
|
||||
**Failure:**
|
||||
```
|
||||
[CONFIRM a1b2c3d4] FAILED: Invalid schedule format
|
||||
```
|
||||
|
||||
### API Response with Confirmation Tracking
|
||||
|
||||
When an API request triggers a DM, the response includes the request ID
|
||||
for tracking:
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "a1b2c3d4",
|
||||
"status": "pending",
|
||||
"dm_sent": true,
|
||||
"confirmation_url": "/api/box/requests/a1b2c3d4"
|
||||
}
|
||||
```
|
||||
|
||||
**Check confirmation status:**
|
||||
```
|
||||
GET /api/box/requests/{request_id}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "a1b2c3d4",
|
||||
"status": "confirmed", // pending | confirmed | failed | timeout
|
||||
"created_at": "2026-10-04T02:30:00Z",
|
||||
"confirmed_at": "2026-10-04T02:30:15Z",
|
||||
"attempts": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Pending Request Sweeper
|
||||
|
||||
A background job (every minute) checks for unconfirmed requests:
|
||||
- If `now - created_at > timeout` (default 5m) and status is `pending`:
|
||||
- Retry DM (if attempts < 3)
|
||||
- Else mark `timeout`, log to audit, optionally alert
|
||||
|
||||
### When to Require Confirmation
|
||||
|
||||
Not all API calls need it. Use confirmation for:
|
||||
- ✅ Timer create/delete (critical, hard to undo)
|
||||
- ✅ Job trigger (should verify it started)
|
||||
- ❌ Timer list (read-only, no side effects)
|
||||
- ❌ Job status check (read-only)
|
||||
|
||||
**Rule:** If the API call has side effects (creates, deletes, modifies),
|
||||
it requires confirmation. Reads do not.
|
||||
|
||||
### Integration with JOB System
|
||||
|
||||
The JOB system already uses `[JOB id]` → `[RESULT id]`. This is a specific
|
||||
case of the general confirmation pattern:
|
||||
- `[JOB id]` = `[REQ id]` with type=job
|
||||
- `[RESULT id]` = `[CONFIRM id]` with result data
|
||||
|
||||
The confirmation system generalizes this to all API-triggered DMs.
|
||||
|
||||
Reference in New Issue
Block a user