Kontrak Agen OpenSpec
Antarmuka yang dapat dibaca mesin dari CLI openspec, diverifikasi terhadap src/ (audit capstone, 2026-06-11). Setiap bentuk di bawah ini didokumentasikan dari kode yang mengeluarkannya.
1. Konvensi Umum
- Satu dokumen JSON per pemanggilan. Dalam mode
--json, stdout membawa tepat satu dokumen JSON (dicetak rapi dengan spasi 2). Prosa manusia, spinner, dan banner penyimpanan dikirim ke stderr. - Banner penyimpanan. Dalam mode manusia, root yang dipilih penyimpanan mencetak
Using OpenSpec root: <id> (<path>)ke stderr. Tidak pernah dicetak dalam mode JSON. - Penulisan huruf kunci bergantung pada permukaan (lihat Inkonsistensi yang Diketahui): payload store/doctor/context menggunakan
snake_case; payload alur kerja (status,instructions,new change,validate,list) menggunakancamelCase, kecuali objekrootyang disematkan, yang selalu menggunakanstore_id. - Kunci opsional dihilangkan, bukan null, di sebagian besar payload (misalnya,
root.store_id,member.path). Pengecualian yang menggunakannulleksplisit disebutkan per bentuk (store doctorgit.*, payload kegagalan).
2. Amplop diagnostik
Satu bentuk amplop dibagikan oleh setiap diagnostik yang dapat dibaca mesin (StoreDiagnostic):
{
"severity": "error" | "warning" | "info",
"code": "snake_case_string",
"message": "human sentence",
"target": "dotted.surface (optional)",
"fix": "one actionable sentence/command (optional)"
}Diagnostik muncul di dua posisi: array status (status: StoreDiagnostic[] di tingkat atas atau per entri) untuk temuan kesehatan, dan kesalahan yang dilempar yang dikonversi menjadi array status satu elemen pada kegagalan perintah.
3. Pemilihan root dan RootOutput
Semua perintah resolusi root (list, show, validate, status, instructions, instructions apply, instructions archive, new change, archive, doctor, context, schemas) menyelesaikan satu root OpenSpec dengan satu prioritas:
--store <id>→ root toko terdaftar (source: "store").- Jika tidak, leluhur terdekat dengan
openspec/: bentuk perencanaan →source: "nearest"(penunjukstore:diabaikan dengan peringatan stderr); direktori hanya-konfigurasi dengan penunjukstore:yang valid → toko tersebut,source: "declared". - Tidak ada root terdekat +
defaultStoreglobal diatur (openspec config set defaultStore <id>) → toko tersebut,source: "global_default"; id usang gagal dengan kesalahan toko mendasar danfixyang menyebutkanopenspec config unset defaultStore. - Tidak ada root terdekat, tidak ada default + toko terdaftar ada → kesalahan
no_root_with_registered_stores. - Tidak ada root, tidak ada default, tidak ada toko: perintah dapat memperlakukan cwd sebagai
source: "implicit";doctor,context,list, danvalidatemassal sebaliknya gagal denganno_openspec_root.listmempertahankan fallback implisit untuk proyek warisan denganopenspec/project.md.
Payload JSON yang berhasil biasanya menanamkan root; schemas --json yang berhasil sengaja tetap berupa array telanjang kompatibilitas yang didokumentasikan dalam §4.13:
"root": { "path": "/abs/path", "source": "store" | "declared" | "global_default" | "nearest" | "implicit", "store_id": "id (only when store-selected)" }Kontrak kegagalan root: dalam mode JSON, kegagalan resolusi mencetak { ...commandNullShape, "status": [diagnostic] } ke stdout dan keluar dengan kode 1.
4. Bentuk JSON Perintah
4.1 list --json
{ "changes": [ { "name", "completedTasks", "totalTasks", "lastModified", "status": "no-tasks"|"complete"|"in-progress" } ], "root": RootOutput } — perhatikan bahwa status per-perubahan adalah enum string di sini. --specs: { "specs": [ { "id", "requirementCount" } ], "root" }.
4.2 show <item> --json
Perubahan: { "id", "title", "deltaCount", "deltas": [...], "root" }. Spesifikasi: { "id", "title", "overview", "requirementCount", "requirements": [...], "metadata": { "version", "format", "sourcePath"? }, "root" }.
4.3 validate --json
{ "items": [ { "id", "type": "change"|"spec", "valid", "issues": [ { "level", "path", "message", "line"?, "column"? } ], "durationMs" } ], "summary": { "totals": {items,passed,failed}, "byType": {...} }, "version": "1.0", "root" }. Keluar dengan 1 ketika ada item yang gagal.
4.4 status --json
{ "changeName", "schemaName", "planningHome"?: { "kind", "root", "changesDir", "defaultSchema" }, "changeRoot", "artifactPaths": { "<id>": {outputPath, resolvedOutputPath, existingOutputPaths} }, "nextSteps": ["..."], "actionContext": { "mode": "repo-local", "sourceOfTruth": "repo", "planningArtifacts", "linkedContext", "allowedEditRoots", "requiresAffectedAreaSelection", "constraints" }, "isPlanningComplete", "isComplete", "applyRequires", "artifacts": [ {id, outputPath, status: "done"|"skipped"|"ready"|"blocked", requires, missingDeps?} ], "root" }. isPlanningComplete berarti setiap artefak perencanaan yang tidak dilewati ada; artefak yang dilewati dihitung terpenuhi tanpa dibuat. Ini tidak berarti tugas implementasi selesai. isComplete dipertahankan sebagai alias kompatibilitas dengan nilai yang sama. requires setiap artefak adalah id dependensi langsungnya (ada untuk setiap status, sehingga himpunan yang diperlukan secara transitif dapat dihitung bahkan ketika artefak done); missingDeps hanya muncul ketika blocked. Array artifacts berada dalam urutan dependensi, dengan urutan deklarasi artifacts: skema memecah hubungan antara artefak yang menjadi siap pada waktu yang sama (bukan alfabetis), sehingga entri ready pertama adalah artefak yang akan ditulis selanjutnya; missingDeps menggunakan urutan yang sama. "skipped" menandai artefak yang jalur generates-nya berada di bawah specs/ dalam perubahan yang .openspec.yaml-nya mendeklarasikan skip_specs: true; ini memenuhi dependensi tetapi tidak boleh dibuat. Tidak ada perubahan aktif: { "changes": [], "message", "root" }, keluar dengan 0.
--all (batch, eksklusif bersama --change — menggabungkannya adalah kesalahan dengan null-shape { "changes": [], "root": null, "status": [d] }): { "changes": [ <objek status per-perubahan, tanpa root per-perubahan>, ... ], "root" }, diurutkan berdasarkan nama perubahan. Perubahan yang gagal dimuat berkontribusi { "changeName", "status": [d] } sebagai pengganti; pembersihan berlanjut, mempertahankan amplop lengkap, dan keluar dengan 1 baik dalam mode teks maupun JSON. --schema yang tidak valid mengakhiri seluruh pemanggilan dengan null-shape, bahkan ketika tidak ada perubahan yang ada.
4.5 instructions <artifact> --json
{ "changeName", "artifactId", "schemaName", "changeDir", "planningHome"?, "outputPath", "resolvedOutputPath", "existingOutputPaths", "description", "instruction"?, "context"?, "rules"?, "references"?: ReferenceIndexEntry[], "skipped"?, "warning"?, "template", "dependencies": [{id,done,path,description,skipped?}], "unlocks", "root" }. unlocks mencantumkan artefak yang membuatnya siap, dalam urutan deklarasi skema (urutan yang sama yang direkomendasikan status). "skipped": true (dengan "warning") muncul ketika perubahan mendeklarasikan skip_specs: true dan artefak ini dilewati — jangan buat file-nya. Entri dependensi dengan skipped: true terpenuhi tanpa file — jangan coba baca jalurnya.
ReferenceIndexEntry: { "store_id", "root"?, "specs"?: [{id,summary}], "fetch"?, "status": [] } — entri yang diselesaikan membawa root/specs/fetch; yang belum diselesaikan membawa store_id + status peringatan. Indeks dibatasi hingga 50KB (reference_index_truncated).
4.6 instructions apply --json
{ "changeName", "changeDir", "schemaName", "contextFiles": { "<artifactId>": ["/abs", ...] }, "progress": {total,complete,remaining}, "tasks": [{id,description,done}], "state": "blocked"|"all_done"|"ready", "missingArtifacts"?, "missingPrerequisites"?, "warnings"?, "instruction", "references"?, "context"?, "operationGuidance"?, "root" }. missingArtifacts adalah apa yang menghalangi penerapan ( apply.requires skema); missingPrerequisites adalah segala sesuatu yang masih harus dibangun sebelum penerapan dapat berjalan, dalam urutan build - penutupan transitif dari yang memerlukan tersebut, sehingga bisa menjadi daftar yang lebih panjang. warnings mencantumkan masalah non-penghalang dengan perubahan itu sendiri - saat ini, perubahan yang siap diimplementasikan tanpa delta spesifikasi dan tanpa skip_specs: true, keadaan yang ditolak openspec validate. Kedua bidang root opsional (context, operationGuidance) dibaca dari root yang dipilih pada setiap pemanggilan. context adalah input tingkat prompt yang wajib yang fakta proyek, konvensi, dan kendala yang relevan harus diterapkan; operationGuidance adalah input konsultatif yang masukannya diikuti hanya ketika berlaku dan kompatibel dengan alur kerja bawaan. Keduanya tetap terpisah dari state, tugas, kemajuan, file konteks, dan instruksi bawaan.
4.7 instructions archive --json
{ "changeName", "context"?, "operationGuidance"?, "root" }. Memerlukan --change yang valid di root repositori/toko yang diselesaikan dan menggunakan semantik bimbingan konteks yang diperlukan/konsultatif yang sama seperti apply. Ini adalah permukaan input runtime baca-saja: ini tidak mengembalikan alur kerja arsip statis, memeriksa atau menggabungkan delta spesifikasi, menulis spesifikasi utama, atau memindahkan perubahan.
4.8 new change <name> --json
Berhasil: { "change": { "id", "path", "metadataPath", "schema" }, "root" }. Gagal: { "change": null, "status": [d] }, keluar dengan 1.
4.9 archive <name> --json
Berhasil: { "archive": { "change", "archivedAs": "YYYY-MM-DD-name", "path", "specsUpdated", "totals"?, "warnings"? }, "root" }. Gagal: { "archive": null, "root"?, "status": [d] }, keluar dengan 1. specsUpdated benar hanya ketika setidaknya satu file spesifikasi ditulis atau pensiun (kemampuan yang persyaratan terakhirnya dihapus oleh perubahan memiliki spesifikasinya dihapus, yang memerlukan retire_capabilities: true di .openspec.yaml perubahan; setiap pensiunan dinamai dalam warnings, dengan perintah pemulihan Git yang dapat disalin hanya ketika spesifikasinya hidup di checkout pemanggil); perubahan yang sudah tersinkronisasi diarsipkan dengan total nol dan pengabaian yang tercantum dalam warnings. Mode JSON secara ketat non-interaktif: setiap titik prompt menjadi kode archive_*.
4.10 doctor --json
{ "root": { "path", "source", "store_id"?, "healthy", "status": [] }, "store": { "id", "metadata": {present,valid,remote?}, "origin_url"?, "drift"?: {ahead,behind}, "status": [] } | null, "references": [...], "status": [] }. drift (hanya hadir untuk checkout toko berbasis git yang memiliki referensi pelacakan hulu) adalah jumlah maju/mundur terhadap hulu terakhir yang diambil, bukan hulu langsung. Temuan kesehatan dari tingkat keparahan apa pun keluar dengan 0. Payload kegagalan: { "root": null, "store": null, "references": [], "status": [d] }, keluar dengan 1.
4.11 context --json
{ "root": { "path", "source", "store_id"?, "role": "openspec_root" }, "members": [ { "role": "referenced_store", "id", "path"?, "remote"?, "fetch"?, "status": [] } ], "status": [] }. TERSEDIA = path hadir DAN status kosong. --code-workspace <path> menulis {folders:[{name,path}]} (hanya toko referensi yang tersedia, awalan ref:); dalam mode JSON, penulisan berjalan sebelum pencetakan sehingga stdout berisi tepat satu dokumen bahkan pada kegagalan penulisan. Kegagalan: { "root": null, "members": [], "status": [d] }, keluar dengan 1.
4.12 store ... --json
setup/register: { "store": {id, root, metadata_path?}, "registry": {path, registered, already_registered}, "git": {is_repository, initialized, committed}, "created_files": [], "status": [] }. unregister/remove: { "store", "registry": {path, removed}, "files": {deleted, deleted_path, left_on_disk}, "status": [] }. list: { "stores": [{id, root}], "status": [] }. doctor: { "stores": [ { id, root, metadata_path?, openspec_root: {...healthy, status}, metadata: {present, valid, id?, remote}, git: {is_repository, has_commits, has_uncommitted_changes, has_remote, origin_url}, status } ], "status": [] } (null = tidak diketahui/belum diperiksa). Temuan kesehatan keluar dengan 0; kegagalan keluar dengan 1 dengan null-shape yang sesuai. Pembatalan prompt keluar dengan 130.
4.13 schemas --json / templates --json
schemas: keberhasilan tetap berupa array telanjang [ {name, description, artifacts, source} ]; ini menyelesaikan prioritas pemilihan root kanonik dan menerima --store <id>. Kegagalan pemilihan root: { "schemas": [], "root": null, "status": [d] }, keluar dengan 1. templates: objek berkunci { "<artifactId>": {path, source} }, masih berbasis cwd tanpa kunci root/status.
5. Kontrak exit-code
| Situasi | Exit | Stdout |
|---|---|---|
| Berhasil, termasuk temuan kesehatan (doctor/context/store doctor) | 0 | payload-nya |
Kegagalan perintah dalam mode --json | 1 | satu dokumen JSON dengan status: [d] dan null-shape perintah |
validate dengan item yang gagal | 1 | laporan lengkap |
Pembatalan prompt (grup store, mode human) | 130 | hanya stderr |
6. Katalog kode diagnostik
Resolution
no_openspec_root, no_root_with_registered_stores, no_registered_stores, unknown_store, store_identity_mismatch, unhealthy_store_root, store_path_not_supported, invalid_store_pointer, initiative_option_removed, areas_option_removed; diteruskan: invalid_store_id, invalid_store_registry, invalid_store_metadata.
Kesehatan OpenSpec-root (error, tidak ada perbaikan)
openspec_store_root_missing, openspec_store_root_not_directory, openspec_root_missing, openspec_root_not_directory, openspec_config_missing, openspec_config_not_file, openspec_specs_not_directory, openspec_changes_not_directory, openspec_archive_not_directory. Selama beta stores, openspec/specs/, openspec/changes/, dan openspec/changes/archive/ mungkin tidak ada di root yang sehat; mereka hanya menjadi error kesehatan jika ada tetapi bukan direktori.
Store registry/identity/state
invalid_store_id, invalid_store_registry, invalid_store_metadata, store_registry_busy, store_not_found, no_store_registry, store_registry_changed, store_metadata_missing, store_metadata_id_mismatch, store_metadata_invalid, store_id_conflict, store_path_conflict, store_already_registered (info).
Store setup/register/remove
store_setup_id_required, store_setup_path_required, store_setup_path_not_directory, store_setup_inside_git_repo, store_setup_non_empty_directory, store_setup_cancelled, store_path_required, store_path_missing, store_path_not_directory, store_root_pointer_declared, store_register_root_unhealthy, store_register_identity_confirmation_required, store_register_cancelled, store_remote_empty, store_remote_requires_hand_edit, store_remove_confirmation_required, store_remove_cancelled, store_remove_path_not_directory, store_remove_metadata_missing, store_root_missing (peringatan di remove, error di doctor), store_root_not_directory.
Store git
store_git_init_failed, store_git_identity_missing, store_git_commit_failed, store_git_no_commits (peringatan), store_clone_fragile_directories (peringatan), store_remote_divergence (info, doctor), store_checkout_drift (info, doctor).
Referensi (peringatan)
reference_invalid_id, reference_registry_unreadable, reference_unresolved, reference_root_unhealthy, reference_index_truncated.
Hubungan (peringatan; doctor; context hanya menyimpan yang registry)
relationship_registry_unreadable, root_pointer_ignored, root_pointer_invalid, pointer_declarations_inert.
Archive (mode JSON)
archive_change_name_required, archive_change_not_found, archive_change_symlink, archive_validation_failed, archive_confirmation_required, archive_tasks_incomplete, archive_spec_update_failed, archive_spec_validation_failed, archive_target_exists, archive_error.
Penulisan konteks
context_file_exists, context_output_dir_missing.
Fallback
doctor_failed, context_failed, store_error, change_error, archive_error.
Inkonsistensi yang diketahui
Tercatat oleh audit capstone; penggantian nama kunci yang dipublikasikan adalah keputusan produk yang ditunda setelah rilis ini:
Dalam modeDiperbaiki di putaran gauntlet capstone: item--json, beberapa jalur kegagalan hanya mencetak stderr tanpa dokumen JSON.show/validateyang tidak dikenal dan ambigu mengeluarkan{status:[{code: unknown_item | ambiguous_item, ...}]}; error yang dilempar distatus/instructions/list/show/validatediarahkan melalui pembantu kegagalan yang sadar JSON (null-shape perintah +status);store <subperintah tidak dikenal> --jsonmengeluarkan{status:[{code: unknown_store_subcommand}]};listmembawa null-shape{changes|specs: [], root: null}pada kegagalan resolusi.store_root_missingdikeluarkan dengan dua tingkat keparahan (peringatan di remove, error di store doctor) — bergantung pada konteks, didokumentasikan di atas.- snake_case (keluarga store) vs camelCase (keluarga workflow) casing kunci;
root.store_idadalah snake_case di mana-mana. - Empat deklarasi tipe envelope yang paralel ada di src; diagnostik arsip tidak pernah membawa
target. list --jsonmenggunakan kembali kuncistatussebagai enum string per change.- Hanya output
validateyang membawa fieldversion. templatesmengabaikan pemilihan root (berbasis cwd, tanpa--store).- Bentuk kata benda yang sudah tidak digunakan lagi (subperintah
change/spec) mengeluarkan payload tanpa envelope tanparoot/status.