# Task Manager API — Dokumentasi & Kontrak

**Base URL:** `https://task.masmuf.cloud/api/v1`
**Media type:** `application/json`
**Versi kontrak:** 1.2 (28-08-2026)

---

## 1. State Machine

Alur task mengikuti **4 state berurutan**:

```
backlog ──▶ todo ──▶ on_progress ──▶ done
```

| State | Makna |
|---|---|
| `backlog` | Antrian ide/tugas yang belum dijadwalkan |
| `todo` | Siap dikerjakan |
| `on_progress` | Sedang dikerjakan |
| `done` | Selesai |

**Aturan transisi:**
- Boleh maju satu langkah (`backlog→todo→on_progress→done`)
- Boleh mundur satu langkah (revisi: `done→on_progress`, `on_progress→todo`, `todo→backlog`)
- AI agent disarankan **tidak melompat** (mis. `backlog→done` langsung) tanpa alasan tertulis di `description`
- Nilai `state` selain 4 di atas → **400**

---

## 2. Kontrak Data

### Objek Task

```json
{
  "id": 3,
  "title": "Implementasi fitur X",
  "description": "Detail task / catatan hasil kerja",
  "state": "todo",
  "instansi": "pribadi",
  "app_name": "mas-dana",
  "service_name": "api",
  "category": "new_feature",
  "created_at": "2026-08-28T07:47:28.865839+00:00",
  "updated_at": "2026-08-28T08:10:00.000000+00:00"
}
```

| Field | Tipe | Wajib | Aturan |
|---|---|---|---|
| `id` | integer | (server) | auto-increment |
| `title` | string | ✅ | non-empty; `trim()`; max 500 karakter |
| `description` | string | ❌ | default `""`; `trim()` |
| `state` | string | ❌ | default `backlog`; ∈ 4 state |
| `instansi` | string | ❌ | default `"pribadi"`; ∈ `["kantor","pribadi"]` |
| `app_name` | string | ❌ | default `""`; label nama aplikasi |
| `service_name` | string | ❌ | default `""`; label nama service |
| `category` | string | ❌ | default `"new_feature"`; ∈ `["bug_fix","new_feature"]` |
| `created_at` | string (ISO-8601, UTC) | (server) | read-only |
| `updated_at` | string (ISO-8601, UTC) | (server) | berubah tiap update |

### Format Error (konsisten semua endpoint)

```json
{ "error": "pesan deskriptif" }
```

| Kode | Kondisi |
|---|---|
| `400` | body invalid / title kosong / state tidak dikenal / tidak ada field diupdate |
| `404` | task id tidak ditemukan |
| `405` | method tidak diizinkan |
| `500` | error internal (DB dsb.) |

---

## 3. Endpoint & Kontrak Request/Response

### 3.1 `GET /states`

**Response 200:**
```json
{ "states": ["backlog", "todo", "on_progress", "done"] }
```

---

### 3.2 `GET /tasks`

Query param (opsional, kombinasi bebas — semua AND):
| Param | Tipe | Aturan |
|---|---|---|
| `state` | string | harus ∈ 4 state, selain itu **400** |
| `instansi` | string | harus ∈ `kantor`/`pribadi`, selain itu **400** |
| `category` | string | harus ∈ `bug_fix`/`new_feature`, selain itu **400** |
| `app_name` | string | partial match (ILIKE) |
| `service_name` | string | partial match (ILIKE) |
| `search` | string | case-insensitive ILIKE pada title/description |

**Response 200:**
```json
{
  "count": 2,
  "tasks": [ { "id": 1, "title": "...", "description": "", "state": "backlog", "instansi": "pribadi", "app_name": "", "service_name": "", "category": "new_feature", "created_at": "...", "updated_at": "..." } ]
}
```
Urutan: `id DESC` (terbaru dulu).

**Contoh filter:**
```bash
curl "https://task.masmuf.cloud/api/v1/tasks?instansi=kantor&category=bug_fix"
curl "https://task.masmuf.cloud/api/v1/tasks?app_name=mas-dana"
```

**Error 400:**
```json
{ "error": "state harus salah satu dari ['backlog', 'todo', 'on_progress', 'done']" }
```

---

### 3.3 `POST /tasks` — create

**Request body:**
```json
{
  "title": "Implementasi fitur X",       // wajib, non-empty setelah trim
  "description": "Detail task",          // opsional, default ""
  "state": "todo",                       // opsional, default "backlog"
  "instansi": "kantor",                  // opsional, default "pribadi" ("kantor"|"pribadi")
  "app_name": "mas-dana",                // opsional, default ""
  "service_name": "api",                 // opsional, default ""
  "category": "new_feature"              // opsional, default "new_feature" ("bug_fix"|"new_feature")
}
```

**Response `201 Created`:**
```json
{ "task": { "id": 3, "title": "Implementasi fitur X", "description": "Detail task", "state": "todo", "created_at": "...", "updated_at": "..." } }
```

**Error 400:** `{"error":"title wajib diisi"}` / `{"error":"state harus salah satu dari [...]"}`

---

### 3.4 `PATCH /tasks/{id}` — update / pindah state

**Request body** (semua opsional, minimal 1 field):
```json
{
  "title": "Judul baru",            // opsional; non-empty
  "description": "Catatan baru",    // opsional
  "state": "on_progress",           // opsional; ∈ 4 state
  "instansi": "pribadi",            // opsional; ∈ ["kantor","pribadi"]
  "app_name": "app-x",              // opsional
  "service_name": "svc-y",          // opsional
  "category": "bug_fix"             // opsional; ∈ ["bug_fix","new_feature"]
}
```

**Response 200:**
```json
{ "task": { "id": 3, "title": "Judul baru", "description": "Catatan baru", "state": "on_progress", "created_at": "...", "updated_at": "..." } }
```

**Error:**
- `400` — `{"error":"tidak ada field yang diupdate"}` / `{"error":"title tidak boleh kosong"}` / `{"error":"state harus salah satu dari [...]"}`
- `404` — `{"error":"task tidak ditemukan"}`

> ℹ️ **Catatan idempotensi:** PATCH state ke nilai yang sama = sukses 200, tidak error.

---

### 3.5 `DELETE /tasks/{id}`

**Response 200:**
```json
{ "deleted": true, "id": 3 }
```

**Error 404:** `{"error":"task tidak ditemukan"}`

---

## 4. Contoh Skenario Agent (contract test)

**Siklus hidup lengkap:**

```bash
# 1. Baca antrian
curl "https://task.masmuf.cloud/api/v1/tasks?state=backlog"

# 2. Ambil & mulai (wajib PATCH title/description/state — minimal state)
curl -X PATCH https://task.masmuf.cloud/api/v1/tasks/3 \
  -H "Content-Type: application/json" \
  -d '{"state":"on_progress"}'

# 3. Selesai + catat hasil di description
curl -X PATCH https://task.masmuf.cloud/api/v1/tasks/3 \
  -H "Content-Type: application/json" \
  -d '{"state":"done","description":"Selesai: PR #12 merged, test 14/14 hijau"}'

# 4. Cek state akhir
curl "https://task.masmuf.cloud/api/v1/tasks?state=done"
```

**Assertion yang wajib lolos (kontrak):**
1. `POST` tanpa `title` → 400 `{"error":"title wajib diisi"}`
2. `POST` dengan `state:"lalala"` → 400
3. `GET /tasks?state=lalala` → 400
4. `PATCH` task yang tidak ada → 404
5. `PATCH` tanpa field → 400
6. `DELETE` dua kali berturut-turut → 200 lalu 404
7. Semua response punya key sesuai schema (tidak ada key ekstra yang berubah bentuk)
8. `state` pada task hasil create selalu ∈ 4 state

---

## 5. Panduan Integrasi AI Agent (Claude Code / Hermes)

1. **Sebelum kerja:** `GET /api/v1/tasks?state=backlog` (dan `?state=todo`)
2. **Ambil task:** `PATCH {id}` → `state:"on_progress"`
3. **Selesai:** `PATCH {id}` → `state:"done"` + tulis ringkasan hasil di `description`
4. **Jangan** menghapus task orang lain tanpa konfirmasi; `DELETE` hanya untuk task yang jelas-jelas basi/duplikat
5. Semua request pakai header `Content-Type: application/json`
6. Tangani `429`/`5xx` dengan retry eksponensial (saat ini tidak ada rate limit, tapi tetap defensif)

---

## 6. Infrastruktur

| Komponen | Detail |
|---|---|
| App | Flask `task-manager` (container) |
| DB | PostgreSQL — `masmuf_postgres` / db `task_manager` / user `task_manager` |
| Proxy | Traefik, TLS Let's Encrypt (`myresolver`) |
| Web UI | https://task.masmuf.cloud (kanban 4 kolom) |
| Debug lokal | port 5199 (hanya localhost VPS) |

**Keamanan:** saat ini **tanpa auth** (memudahkan integrasi agent). Rencana jika publik luas:
API key sederhana (`X-API-Key`) atau Basic Auth di Traefik. Perubahan akan dibump ke kontrak v1.1.
