hanoman 0.1.39 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -168,6 +168,7 @@ dan tak memakai agent token.
168
168
  | `GET /api/specs` | `backlog:read` | backlog. Filter: `project`, `source`, `q`, `stage`, `priority`, `startable=true`, `dateField=created\|started` + `from`/`to` (`YYYY-MM-DD`, inklusif), `page`, `limit`. |
169
169
  | `POST /api/specs` | `backlog:write` | buat backlog item — bentuk payload di §7. |
170
170
  | `PATCH /api/specs/:id` | `backlog:write` | ubah item; konten hanya selagi belum dimulai. |
171
+ | `POST /api/specs/:id/done` | `backlog:write` | tandai item **selesai** tanpa menjalankan sesi — untuk pekerjaan yang beres di luar hanoman. Body `{ reason?: string (≤280), confirm?: boolean }`, keduanya opsional; balasannya `Spec` yang sudah `stage:"done"`. Tak menjalankan maupun menghentikan sesi apa pun. |
171
172
  | `GET /api/specs/:id/docs` | `backlog:read` | dokumen yang ditulis sesi item itu. |
172
173
  | `GET /api/specs/:id/review` | `backlog:read` | diff hasil kerja sesi. |
173
174
  | `GET /api/projects/:id/docs` | `docs:read` | index Source of Truth project. |
@@ -271,7 +272,9 @@ capability-nya. Capability menjawab "boleh?", bukan "sebaiknya?".
271
272
  | `POST /api/lead/decisions` | putusannya bisa **menggerakkan sesi** (integrate ke main, menghentikan sesi) dan selalu melahirkan baris jejak permanen (ADR-0091/0098). |
272
273
 
273
274
  Perlakukan `POST /api/specs/:id/integrate`, `DELETE /api/specs/:id`, dan perubahan `stage` dengan
274
- disiplin yang sama: ketiganya mengubah sejarah git atau membuang pekerjaan.
275
+ disiplin yang sama: ketiganya mengubah sejarah git atau membuang pekerjaan. `POST /api/specs/:id/done`
276
+ tak menyentuh git, tetapi ia **menyatakan pekerjaan orang lain selesai** — pakai hanya bila kamu punya
277
+ buktinya, dan tulis buktinya di `reason`.
275
278
 
276
279
  **Preseden yang mengikat:** MCP server resmi (`hanoman mcp`, §13) sengaja **tak punya tool** untuk
277
280
  satu pun dari yang di atas — batasnya ada di katalog tool, bukan di token. Token yang punya
@@ -286,6 +289,7 @@ memanggilnya; jangan lakukan tanpa manusia.
286
289
  | `q` mencari di `id`, `title`, dan `objective` saja — ia **tak menyentuh `payload`** | untuk mencari isi brief/QA, ambil itemnya lalu baca `payload` sendiri |
287
290
  | `id` dan `stage` yang kamu sertakan di `POST /api/specs` **dibuang diam-diam** — tak ada galat | `id` diterbitkan server (`SPEC-nnn` berikutnya), `stage` selalu mulai `brainstorming`. Untuk mengubah stage pakai `PATCH /api/specs/:id`, dan ia hanya boleh **mundur** (ADR-0027) |
288
291
  | **`GET /api/specs/:id` tidak ada** | `GET /api/specs?q=SPEC-489` lalu cocokkan `id` **persis** — `q` itu substring, jadi ia bisa mengembalikan lebih dari satu |
292
+ | `POST /api/specs/:id/done` menjawab **409 `{"error":"confirm-required","session":{...}}`** — itu bukan penolakan, melainkan pemberitahuan bahwa masih ada **sesi hidup** untuk item itu | putuskan dulu apakah sesi itu memang sudah tak relevan; bila ya, kirim ulang dengan `{"confirm": true}`. Sesinya **tidak** ikut dihentikan — tutup sesinya sendiri bila perlu. 409 `{"error":"backlog item sudah selesai"}` berarti item itu memang sudah `done` |
289
293
  | daftar mengembalikan amplop `{ items, total, page, pageSize }` | jangan perlakukan responsnya sebagai array |
290
294
  | tanpa `limit`, daftar mengembalikan **seluruh** item dalam satu halaman | kirim `limit` untuk backlog besar |
291
295
  | **`GET /api/notifications` adalah pengecualiannya**: tanpa `limit` ia mengembalikan **50 teratas**, bukan seluruhnya — angka penuhnya ada di `total` | kirim `page`/`limit` bila kamu butuh riwayat lama; jangan simpulkan `items.length` = seluruh notifikasi |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanoman",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "description": "Orchestrator + dashboard workflow docs-driven untuk sesi Claude Code / Codex",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,14 @@
1
+ -- SPEC-799 · ADR-0119 · tombstone sync: penghapusan sebagai keadaan pertama-kelas.
2
+ CREATE TABLE "SyncTombstone" (
3
+ "id" TEXT NOT NULL PRIMARY KEY,
4
+ "entity" TEXT NOT NULL,
5
+ "recordId" TEXT NOT NULL,
6
+ "version" INTEGER NOT NULL,
7
+ "data" JSONB NOT NULL,
8
+ "deletedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ "deviceId" TEXT
10
+ );
11
+ CREATE UNIQUE INDEX "SyncTombstone_entity_recordId_key" ON "SyncTombstone"("entity", "recordId");
12
+
13
+ -- Additive & default aman: seluruh baris feed lama terbaca sebagai "upsert".
14
+ ALTER TABLE "SyncLog" ADD COLUMN "op" TEXT NOT NULL DEFAULT 'upsert';
@@ -0,0 +1,9 @@
1
+ -- SPEC-804 · ADR-0120 · jejak penandaan selesai manual sebagai kolom.
2
+ --
3
+ -- Ditulis tangan (bukan `migrate dev`): worktree tetangga membuat `migrate dev` me-reset DB saat
4
+ -- ada drift. ADITIF murni — satu kolom NULLABLE tanpa default, tak ada tabel diredefinisi.
5
+ --
6
+ -- TANPA backfill, sengaja: sebelum spec ini jalur "tandai selesai manual" memang tak ada, jadi
7
+ -- tak ada stempel lama yang bisa dipulihkan. Item lama tetap NULL = "selesai lewat sesi / tak
8
+ -- diketahui", dan itu jawaban yang jujur.
9
+ ALTER TABLE "Spec" ADD COLUMN "manualDone" JSONB;
@@ -63,6 +63,12 @@ model Spec {
63
63
  // site adalah kelas bug SPEC-431/448/475. Tulis-sekali: reopen lalu selesai lagi tak
64
64
  // memindahkannya, cermin `startedAt` = mulai pertama (ADR-0090).
65
65
  doneAt DateTime?
66
+ // SPEC-804 · ADR-0120 · jejak penandaan selesai MANUAL: { at, by, reason? }. Satu kolom, bukan
67
+ // tiga skalar — ketiganya satu peristiwa, dan tiga kolom nullable bisa drift tanpa tipe yang
68
+ // memaksanya konsisten (kelas gagal-senyap ADR-0090/0093/0094/0105). null = item ini tak pernah
69
+ // ditandai manual. Ditimpa tiap penandaan berikutnya: ia menjelaskan keadaan yang BERLAKU;
70
+ // riwayat transisi stage tinggal di SessionResult (ADR-0047). Ikut FIELDS.spec sync.
71
+ manualDone Json?
66
72
  // SPEC-447 · ADR-0093 · id spec lain yang harus SELESAI & commit-nya sudah ada di branch basis
67
73
  // sebelum item ini boleh diluncurkan. Array JSON of string; null/[] = tak bergantung apa pun.
68
74
  // Sengaja kolom, bukan tabel join: SQLite melarang scalar list, `Json` sudah dipakai `payload`,
@@ -385,6 +391,11 @@ model SyncLog {
385
391
  recordId String
386
392
  version Int
387
393
  data Json
394
+ // SPEC-799 · ADR-0119 · jenis peristiwa. `@default("upsert")` membuat seluruh baris feed lama
395
+ // terbaca benar tanpa backfill. Kolom TOP-LEVEL, bukan penanda di dalam `data`: `validateSyncData`
396
+ // menegakkan allowlist atas `data`, jadi penanda di sana membuat client versi lama MELEMPAR →
397
+ // `feedHole` menyala → kursornya tertahan selamanya.
398
+ op String @default("upsert")
388
399
  deviceId String?
389
400
  createdAt DateTime @default(now())
390
401
 
@@ -431,6 +442,27 @@ model SyncConflict {
431
442
  @@unique([entity, recordId])
432
443
  }
433
444
 
445
+ // SPEC-799 · ADR-0119 · LOCAL-ONLY sebagai tabel, tapi maknanya menyeberang: keadaan "record ini
446
+ // dihapus" yang bertahan restart dan menahan setiap upaya membangkitkannya kembali. Hard-delete
447
+ // dipertahankan (bukan soft-delete `deletedAt` per entitas) supaya cascade tingkat-DB tetap bekerja
448
+ // dan tak satu pun query baca yang sudah ada harus berubah — penyaring yang terlewat di bentuk itu
449
+ // gagal SENYAP dengan gejala persis bug yang sedang diperbaiki.
450
+ //
451
+ // `data` = snapshot field tersync tepat sebelum baris dihapus. Ia BUKAN kenyamanan: tanpa snapshot,
452
+ // push delete ke hub versi LAMA (yang membuang `op` sebagai field tak dikenal) berbentuk create
453
+ // tanpa kolom required → P2011 → 500 di setiap siklus push.
454
+ model SyncTombstone {
455
+ id String @id @default(cuid())
456
+ entity String
457
+ recordId String
458
+ version Int // versi record SESUDAH dihapus (= versi terakhirnya + 1)
459
+ data Json
460
+ deletedAt DateTime @default(now())
461
+ deviceId String?
462
+
463
+ @@unique([entity, recordId])
464
+ }
465
+
434
466
  // SPEC-294 · ADR-0072 · LOCAL-ONLY (tak disync, cermin SyncOutbox): antrean durable kandidat peluncuran
435
467
  // scheduler. specId @unique = idempoten satu-sesi-per-spec (ADR-0015). Unit peluncuran selalu Spec.
436
468
  model SchedulerQueueItem {