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.
- package/dist/build-info.json +3 -3
- package/dist/cli.js +25 -3
- package/dist/server.js +594 -241
- package/docs/agent-integration.md +5 -1
- package/package.json +1 -1
- package/prisma/migrations/20260815120000_sync_tombstone/migration.sql +14 -0
- package/prisma/migrations/20260815130000_spec_manual_done/migration.sql +9 -0
- package/prisma/schema.prisma +32 -0
- package/web/assets/{index-BzBNb9qp.js → index-BLt4wnhB.js} +1548 -1547
- package/web/assets/{index-DeGA9N3Z.css → index-BeFLNV7l.css} +1 -1
- package/web/index.html +2 -2
|
@@ -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
|
@@ -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;
|
package/prisma/schema.prisma
CHANGED
|
@@ -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 {
|