---
okf: "0.1"
type: subsystem
visibility: public
status: active
updated: 2026-05-20
links: []
---

### Action Approval Data Model

Actions are JSON files stored in `/opt/homelab/actions/{status}/{action_id}.json`.

#### Statuses
- `pending`: Waiting for operator approval. AI agents create actions in this state.
- `approved`: Approved by operator, ready for execution.
- `rejected`: Rejected by operator, will not be executed.
- `running`: Currently being executed by an agent (e.g. `materializer`).
- `completed`: Successfully executed.
- `failed`: Execution failed.

#### Human-in-the-Loop (HIL) Protocol
1. **Request**: Agent identifies a required change and writes a JSON to `actions/pending/`.
2. **Notification**: System notifies the human operator.
3. **Audit**: Human reviews `details.reason` and `details.diff`.
4. **Authorization**: Human moves file to `approved/`.
5. **Execution**: Agent monitors `approved/` and executes the task.

#### Schema
```json
{
  "action_id": "string",
  "service": "string",
  "node": "string",
  "type": "deploy_service | restart_service | rollback | scale",
  "risk": "nominal | guarded | critical",
  "status": "pending | approved | rejected | ...",
  "created_at": <unix_seconds>,
  "updated_at": <unix_seconds>,
  "details": {
    "image": "string",
    "reason": "string",
    "diff": "string"
  },
  "transition_history": [
    {
      "from": "string | null",
      "to": "string",
      "timestamp": <unix_seconds>,
      "by": "string (system | operator-tg-12345 | webui)"
    }
  ]
}
```

#### Workflow
1. A system component (e.g. `runtime-materializer` or a future analyzer) creates a file in `actions/pending/`.
2. `telegram-bot` detects the file, sends a message to allowed users.
3. Operator clicks "Approve" or "Reject".
4. `telegram-bot` moves the file to `actions/approved/` or `actions/rejected/` atomically, appending a transition to `transition_history`.
5. The responsible agent (e.g. `stability-agent` on the target node) picks up the `approved` action, moves it to `running`, executes it, and finally moves it to `completed` or `failed`.
