Hợp đồng Agent của OpenSpec
Các bề mặt có thể đọc được bởi máy của CLI openspec, được xác minh đối chiếu với src/ (kiểm toán tổng kết, 2026-06-11). Mọi cấu trúc bên dưới đều được tài liệu hóa từ mã phát sinh.
1. Các quy ước chung
- Một tài liệu JSON cho mỗi lần gọi. Ở chế độ
--json, stdout chứa đúng một tài liệu JSON (định dạng đẹp với 2 khoảng trắng). Văn bản cho người dùng, bộ chỉ báo tiến trình và banner cửa hàng được ghi ra stderr. - Banner cửa hàng. Ở chế độ người dùng, root được chọn bởi cửa hàng sẽ in
Using OpenSpec root: <id> (<path>)ra stderr. Không bao giờ in ở chế độ JSON. - Việc viết hoa chữ cái của khóa phụ thuộc vào bề mặt (xem Bất nhất quán đã biết): các payload store/doctor/context sử dụng
snake_case; các payload workflow (status,instructions,new change,validate,list) sử dụngcamelCase, ngoại trừ đối tượngrootnhúng, luôn sử dụngstore_id. - Các khóa tùy chọn bị bỏ qua, không đặt là null, trong hầu hết các payload (ví dụ:
root.store_id,member.path). Các ngoại lệ sử dụngnulltường minh được chỉ ra theo từng cấu trúc (store doctorgit.*, các payload lỗi).
2. Lớp vỏ chẩn đoán
Tất cả các chẩn đoán máy đọc được (StoreDiagnostic) đều dùng chung một hình dạng lớp vỏ:
{
"severity": "error" | "warning" | "info",
"code": "snake_case_string",
"message": "human sentence",
"target": "dotted.surface (optional)",
"fix": "one actionable sentence/command (optional)"
}Chẩn đoán xuất hiện ở hai vị trí: mảng trạng thái (status: StoreDiagnostic[] ở cấp cao nhất hoặc trên mỗi mục) dành cho kết quả kiểm tra sức khỏe, và lỗi được ném ra được chuyển thành mảng status một phần tử khi lệnh thất bại.
3. Chọn thư mục gốc và RootOutput
Tất cả các lệnh phân giải thư mục gốc (list, show, validate, status, instructions, instructions apply, instructions archive, new change, archive, doctor, context, schemas) đều phân giải một thư mục gốc OpenSpec với thứ tự ưu tiên:
--store <id>→ thư mục gốc của store đã đăng ký (source: "store").- Ngược lại, thư mục tiền nhiệm gần nhất có
openspec/: hình dạng planning →source: "nearest"(con trỏstore:bị bỏ qua kèm cảnh báo stderr); thư mục chỉ chứa cấu hình có con trỏstore:hợp lệ → store đó,source: "declared". - Không có thư mục gốc gần nhất + cài đặt
defaultStoretoàn cục (openspec config set defaultStore <id>) → store đó,source: "global_default"; id lỗi thời sẽ thất bại với lỗi store nền vàfixđề cậpopenspec config unset defaultStore. - Không có thư mục gốc gần nhất, không có mặc định + có store đã đăng ký → lỗi
no_root_with_registered_stores. - Không có thư mục gốc, không có mặc định, không có store: các lệnh có thể coi cwd là
source: "implicit"; riêngdoctor,context,list, vàvalidatekhối sẽ thất bại vớino_openspec_root.listgiữ lại phương án dự phòng ẩn dành cho các dự án cũ cóopenspec/project.md.
Payload JSON thành công thường nhúng thư mục gốc; riêng schemas --json thành công cố ý giữ nguyên mảng tương thích trần được ghi nhận ở §4.13:
"root": { "path": "/abs/path", "source": "store" | "declared" | "global_default" | "nearest" | "implicit", "store_id": "id (chỉ khi được chọn qua store)" }Hợp đồng khi thất bại phân giải thư mục gốc: ở chế độ JSON, lỗi phân giải in ra { ...commandNullShape, "status": [diagnostic] } lên stdout và thoát với mã 1.
4. Hình dạng JSON của các lệnh
4.1 list --json
{ "changes": [ { "name", "completedTasks", "totalTasks", "lastModified", "status": "no-tasks"|"complete"|"in-progress" } ], "root": RootOutput } — lưu ý status của mỗi thay đổi ở đây là một enum chuỗi. --specs: { "specs": [ { "id", "requirementCount" } ], "root" }.
4.2 show <item> --json
Thay đổi: { "id", "title", "deltaCount", "deltas": [...], "root" }. Đặc tả: { "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" }. Thoát 1 nếu bất kỳ mục nào thất bại.
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 nghĩa là mọi artifact planning không bị bỏ qua đều tồn tại; artifact bị bỏ qua tính là đã thỏa mãn mà không cần tạo. Điều này không có nghĩa là các tác vụ triển khai đã hoàn thành. isComplete được giữ lại như một bí danh tương thích với cùng giá trị. requires của mỗi artifact là các id phụ thuộc trực tiếp (xuất hiện ở mọi trạng thái, do đó tập phụ thuộc bắc cầu vẫn tính được ngay cả khi artifact đã done); missingDeps chỉ xuất hiện khi blocked. Mảng artifacts được sắp xếp theo thứ tự phụ thuộc, với thứ tự khai báo artifacts: trong schema dùng để giải quyết đồng hạng giữa các artifact sẵn sàng cùng lúc (không bao giờ theo bảng chữ cái), vì vậy mục ready đầu tiên là artifact cần viết tiếp theo; missingDeps dùng chính thứ tự đó. "skipped" đánh dấu artifact có đường dẫn generates nằm dưới specs/ trong thay đổi mà .openspec.yaml khai báo skip_specs: true; nó thỏa mãn phụ thuộc nhưng không được tạo. Không có thay đổi nào hoạt động: { "changes": [], "message", "root" }, thoát 0.
--all (chạy hàng loạt, loại trừ lẫn nhau với --change — kết hợp chúng là lỗi với dạng null { "changes": [], "root": null, "status": [d] }): { "changes": [ <đối tượng trạng thái cho mỗi thay đổi, không có root riêng>, ... ], "root" }, sắp xếp theo tên thay đổi. Một thay đổi không tải được sẽ đóng góp { "changeName", "status": [d] } thay cho vị trí đó; vòng quét tiếp tục, bảo toàn lớp vỏ hoàn chỉnh, và thoát 1 ở cả chế độ văn bản và JSON. Một --schema không hợp lệ khiến toàn bộ lệnh gọi thất bại với dạng null, ngay cả khi không có thay đổi nào.
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 liệt kê các artifact mà artifact này khiến sẵn sàng, theo thứ tự khai báo của schema (cùng thứ tự mà status khuyến nghị). "skipped": true (kèm "warning") xuất hiện khi thay đổi khai báo skip_specs: true và artifact này bị bỏ qua — không tạo các tệp của nó. Một mục phụ thuộc có skipped: true được coi là thỏa mãn mà không cần tệp — không cố gắng đọc đường dẫn của nó.
ReferenceIndexEntry: { "store_id", "root"?, "specs"?: [{id,summary}], "fetch"?, "status": [] } — các mục đã phân giải mang theo root/specs/fetch; các mục chưa phân giải mang store_id + trạng thái cảnh báo. Chỉ mục bị giới hạn 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 là thứ mà apply đang chặn (yêu cầu apply.requires của schema); missingPrerequisites là mọi thứ vẫn cần xây dựng trước khi apply chạy được, theo thứ tự xây dựng — bao đóng bắc cầu của những yêu cầu đó, vì vậy có thể là danh sách dài hơn. warnings liệt kê các vấn đề không gây chặn của chính thay đổi — hiện tại, một thay đổi sẵn sàng triển khai nhưng không có delta spec và không có skip_specs: true, trạng thái mà openspec validate từ chối. Cả hai trường root tùy chọn (context, operationGuidance) được đọc từ thư mục gốc đã chọn trong mỗi lần gọi. context là một đầu vào cấp prompt bắt buộc, chứa các dữ kiện, quy ước và ràng buộc liên quan của dự án phải được áp dụng; operationGuidance là đầu vào tư vấn, các mục của nó chỉ được tuân theo khi phù hợp và tương thích với quy trình tích hợp sẵn. Cả hai đều tách biệt với state, tasks, progress, context files và hướng dẫn tích hợp.
4.7 instructions archive --json
{ "changeName", "context"?, "operationGuidance"?, "root" }. Yêu cầu một --change hợp lệ trong thư mục gốc repo/store đã phân giải và sử dụng cùng ngữ nghĩa ngữ cảnh bắt buộc / hướng dẫn tư vấn như apply. Đây là một bề mặt đầu vào thời gian chạy chỉ-đọc: nó không trả về quy trình lưu trữ tĩnh, không kiểm tra hoặc hợp nhất delta spec, không ghi main spec, và không di chuyển thay đổi.
4.8 new change <name> --json
Thành công: { "change": { "id", "path", "metadataPath", "schema" }, "root" }. Thất bại: { "change": null, "status": [d] }, thoát 1.
4.9 archive <name> --json
Thành công: { "archive": { "change", "archivedAs": "YYYY-MM-DD-name", "path", "specsUpdated", "totals"?, "warnings"? }, "root" }. Thất bại: { "archive": null, "root"?, "status": [d] }, thoát 1. specsUpdated chỉ đúng khi ít nhất một tệp spec được ghi hoặc đã loại bỏ (một năng lực mà yêu cầu cuối cùng bị thay đổi xóa sẽ xóa spec của nó, yêu cầu retire_capabilities: true trong .openspec.yaml của thay đổi; mỗi lần loại bỏ đều được nêu trong warnings, kèm lệnh khôi phục Git có thể dán được chỉ khi spec sống trong bản checkout của người gọi); một thay đổi đã đồng bộ sẽ được lưu trữ với tổng toàn không và các lần bỏ qua được liệt kê trong warnings. Chế độ JSON hoàn toàn không tương tác: mọi điểm prompt trở thành mã 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 (chỉ có ở bản checkout store được git hỗ trợ có tham chiếu theo dõi upstream) là số lượng ahead/behind so với upstream đã fetch lần cuối, không phải so với remote trực tiếp. Các phát hiện sức khỏe ở bất kỳ mức độ nghiêm trọng nào đều thoát 0. Payload thất bại: { "root": null, "store": null, "references": [], "status": [d] }, thoát 1.
4.11 context --json
{ "root": { "path", "source", "store_id"?, "role": "openspec_root" }, "members": [ { "role": "referenced_store", "id", "path"?, "remote"?, "fetch"?, "status": [] } ], "status": [] }. KHẢ DỤNG = đường dẫn tồn tại VÀ trạng thái rỗng. --code-workspace <path> ghi {folders:[{name,path}]} (chỉ các store được tham chiếu khả dụng, tiền tố ref:); ở chế độ JSON, việc ghi chạy trước khi in ra nên stdout chứa đúng một tài liệu ngay cả khi ghi thất bại. Thất bại: { "root": null, "members": [], "status": [d] }, thoát 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 = không xác định / chưa thăm dò). Phát hiện sức khỏe thoát 0; lỗi thoát 1 với dạng null tương ứng. Hủy bỏ prompt thoát 130.
4.13 schemas --json / templates --json
schemas: thành công vẫn là một mảng trần [ {name, description, artifacts, source} ]; nó phân giải thứ tự ưu tiên chọn thư mục gốc chính tắc và chấp nhận --store <id>. Thất bại phân giải thư mục gốc: { "schemas": [], "root": null, "status": [d] }, thoát 1. templates: đối tượng có khóa { "<artifactId>": {path, source} }, vẫn dựa trên cwd, không có khóa root/status.
5. Hợp đồng mã thoát
| Tình huống | Mã thoát | Stdout |
|---|---|---|
| Thành công, bao gồm phát hiện sức khỏe (doctor/context/store doctor) | 0 | payload |
Lỗi lệnh trong chế độ --json | 1 | một tài liệu JSON với status: [d] và null-shape của lệnh |
validate có các mục thất bại | 1 | báo cáo đầy đủ |
Hủy prompt (nhóm store, chế độ người dùng) | 130 | chỉ stderr |
6. Danh mục mã chẩn đoán
Phân giải
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; pass-through: invalid_store_id, invalid_store_registry, invalid_store_metadata.
Sức khỏe OpenSpec-root (lỗi, không có cách sửa)
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. Trong giai đoạn beta của stores, openspec/specs/, openspec/changes/, và openspec/changes/archive/ có thể vắng mặt trong một root khỏe mạnh; chúng chỉ là lỗi sức khỏe khi có mặt nhưng không phải là thư mục.
Registry/identity/trạng thái của Store
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).
Thiết lập/đăng ký/xóa Store
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 (cảnh báo trong remove, lỗi trong doctor), store_root_not_directory.
Store git
store_git_init_failed, store_git_identity_missing, store_git_commit_failed, store_git_no_commits (cảnh báo), store_clone_fragile_directories (cảnh báo), store_remote_divergence (info, doctor), store_checkout_drift (info, doctor).
Tham chiếu (cảnh báo)
reference_invalid_id, reference_registry_unreadable, reference_unresolved, reference_root_unhealthy, reference_index_truncated.
Mối quan hệ (cảnh báo; doctor; context chỉ giữ lại registry)
relationship_registry_unreadable, root_pointer_ignored, root_pointer_invalid, pointer_declarations_inert.
Archive (chế độ 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.
Ghi context
context_file_exists, context_output_dir_missing.
Fallback
doctor_failed, context_failed, store_error, change_error, archive_error.
Các bất nhất đã biết
Được ghi nhận bởi cuộc kiểm toán capstone; việc đổi tên published-key là các quyết định sản phẩm bị hoãn sau bản phát hành này:
Trong chế độĐã sửa trong vòng gauntlet của capstone: các mục không xác định và mơ hồ của--json, một số đường dẫn lỗi chỉ in stderr mà không có tài liệu JSON.show/validatephát ra{status:[{code: unknown_item | ambiguous_item, ...}]}; các lỗi ném ra trongstatus/instructions/list/show/validateđi qua helper lỗi nhận thức JSON (null-shape của lệnh +status);store <unknown subcommand> --jsonphát ra{status:[{code: unknown_store_subcommand}]};listmang null-shape{changes|specs: [], root: null}của nó trong các lỗi phân giải.store_root_missingđược phát ra với hai mức độ nghiêm trọng (cảnh báo trong remove, lỗi trong store doctor) — phụ thuộc ngữ cảnh, đã được tài liệu hóa ở trên.- snake_case (nhóm store) so với camelCase (nhóm workflow) về kiểu viết key;
root.store_idlà snake_case ở mọi nơi. - Bốn khai báo kiểu envelope song song tồn tại trong src; chẩn đoán archive không bao giờ mang
target. list --jsontái sử dụng keystatusnhư một enum chuỗi cho từng change.- Chỉ đầu ra của
validatemang trườngversion. templatesbỏ qua việc chọn root (dựa trên cwd, không có--store).- Các dạng danh từ đã ngừng hỗ trợ (lệnh con
change/spec) phát ra payload không bọc envelope mà không córoot/status.