عقد وكيل OpenSpec
الأسطح القابلة للقراءة آليًا لواجهة openspec السطرية، مُتحقَّق منها مقابل src/ (تدقيق نهائي، 2026-06-11). كل شكل أدناه موثَّق من الكود المُصدر.
1. الاتفاقيات العامة
- مستند JSON واحد لكل استدعاء. في وضع
--json، يحمل stdout مستند JSON واحد بالضبط (منسّق بفراغين). النصوص البشرية، ومؤشرات الدوران، وشريط المتجر تُرسل إلى stderr. - شريط المتجر. في الوضع البشري، يطبع الجذر المُختار من المتجر
Using OpenSpec root: <id> (<path>)إلى stderr. لا يُطبع أبدًا في وضع JSON. - حالة المفاتيح تعتمد على السطح (انظر عدم الاتساق المعروف): حمولات store/doctor/context تستخدم
snake_case؛ حمولات workflow (status،instructions،new change،validate،list) تستخدمcamelCase، باستثناء كائنrootالمضمَّن، الذي يستخدم دائمًاstore_id. - المفاتيح الاختيارية تُحذف ولا تُضبط على null، في معظم الحمولات (مثلًا
root.store_id،member.path). الاستثناءات التي تستخدمnullصراحةً مُذكورة لكل شكل (store doctorgit.*، حمولات الفشل).
2. الغلاف التشخيصي
يتشارك شكل غلاف واحد في كل تشخيص قابل للقراءة آلياً (StoreDiagnostic):
{
"severity": "error" | "warning" | "info",
"code": "snake_case_string",
"message": "human sentence",
"target": "dotted.surface (optional)",
"fix": "one actionable sentence/command (optional)"
}تظهر التشخيصات في موقعين: مصفوفات الحالة (status: StoreDiagnostic[] على المستوى العلوي أو لكل إدخال) لنتائج الصحة، والأخطاء المقذوفة التي تتحول إلى مصفوفة status ذات عنصر واحد عند فشل الأمر.
3. اختيار الجذر و RootOutput
تحل جميع الأوامر التي تحدد الجذر (list, show, validate, status, instructions, instructions apply, instructions archive, new change, archive, doctor, context, schemas) جذر OpenSpec واحد باستخدام أولوية واحدة:
--store <id>→ جذر المتجر المسجل (source: "store").- خلاف ذلك، أقرب سلف يحتوي على
openspec/: شكل التخطيط →source: "nearest"(يتم تجاهل مؤشرstore:مع تحذير stderr)؛ دليل يحتوي فقط على التكوين ومؤشرstore:صالح → ذلك المتجر،source: "declared". - لا يوجد جذر قريب + تعيين
defaultStoreعالمي (openspec config set defaultStore <id>) → ذلك المتجر،source: "global_default"؛ معرف قديم يفشل بخطأ المتجر الأساسي وfixيحددopenspec config unset defaultStore. - لا يوجد جذر قريب، ولا افتراضي + توجد متاجر مسجلة → خطأ
no_root_with_registered_stores. - لا يوجد جذر، ولا افتراضي، ولا متاجر: قد تعامل الأوامر الدليل الحالي (cwd) بأنه
source: "implicit"؛ بدلاً من ذلك تفشلdoctor،context،list، والتحقق الشاملvalidateبالخطأno_openspec_root. تحتفظlistبالسقوط الاحتياطي الضمني للمشاريع القديمة التي تحتوي علىopenspec/project.md.
عادةً ما تضمّن حمولات JSON الناجحة الجذر؛ يبقى schemas --json الناجح عمداً كمصفوفة عارية متوافقة موثقة في §4.13:
"root": { "path": "/abs/path", "source": "store" | "declared" | "global_default" | "nearest" | "implicit", "store_id": "id (only when store-selected)" }عقد فشل الجذر: في وضع JSON، يطبع فشل التحديد { ...commandNullShape, "status": [diagnostic] } على stdout ويخرج بـ 1.
4. أشكال JSON للأوامر
4.1 list --json
{ "changes": [ { "name", "completedTasks", "totalTasks", "lastModified", "status": "no-tasks"|"complete"|"in-progress" } ], "root": RootOutput } — لاحظ أن status لكل تغيير هو تعداد نصي هنا. --specs: { "specs": [ { "id", "requirementCount" } ], "root" }.
4.2 show <item> --json
تغيير: { "id", "title", "deltaCount", "deltas": [...], "root" }. مواصفة: { "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" }. يخرج بـ 1 عندما يفشل أي عنصر.
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 وجود كل عنصر تخطيط غير مُتجاوز؛ تُحتسب العناصر المتجاوزة كمشبعة دون إنشائها. لا تعني أن مهام التنفيذ مكتملة. تم الإبقاء على isComplete كاسم مستعار للتوافق بنفس القيمة. requires لكل عنصر هي معرفات تبعيته المباشرة (موجودة لكل حالة، لذا يمكن حساب المجموعة المطلوبة الانتقالية حتى عندما يكون العنصر done)؛ يظهر missingDeps فقط عندما يكون blocked. مصفوفة artifacts مرتبة حسب الاعتماد، حيث يكسر ترتيب إعلان artifacts: في المخطط التعادل بين العناصر التي تصبح جاهزة في نفس الوقت (وليس أبجدياً)، لذا فإن الإدخال ready الأول هو العنصر الذي يجب كتابته التالي؛ يستخدم missingDeps نفس الترتيب. يحدد "skipped" عنصراً يكون مسار generates الخاص به ضمن specs/ في تغيير يعلن .openspec.yaml الخاص به عن skip_specs: true؛ فهو يشبع التبعيات ولكن لا ينبغي إنشاء ملفاته. لا توجد تغييرات نشطة: { "changes": [], "message", "root" }، يخرج بـ 0.
--all (دفعة، متنافية مع --change — الجمع بينهما خطأ مع الشكل الخالي { "changes": [], "root": null, "status": [d] }): { "changes": [ <كائن حالة لكل تغيير، بدون جذر لكل تغيير>, ... ], "root" }، مرتبة حسب اسم التغيير. يساهم التغيير الذي يفشل في التحميل بـ { "changeName", "status": [d] } مكانه؛ يستمر المسح، يحافظ على الغلاف الكامل، ويخرج بـ 1 في كل من الوضع النصي وJSON. يفشل --schema غير الصالح الاستدعاء بالكامل بالشكل الخالي، حتى عندما لا توجد تغييرات.
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 العناصر التي يجعلها هذه جاهزة، بترتيب الإعلان في المخطط (نفس الترتيب الذي توصي به status). يظهر "skipped": true (مع "warning") عندما يعلن التغيير عن skip_specs: true ويتم تجاوز هذا العنصر — لا تنشئ ملفاته. إشعار التبعية مع skipped: true مشبع دون ملفات — لا تحاول قراءة مساراتها.
ReferenceIndexEntry: { "store_id", "root"?, "specs"?: [{id,summary}], "fetch"?, "status": [] } — تحملEntries المحلولة الجذر/المواصفات/الجلب؛ تحمل Entries غير المحلولة store_id + حالة تحذير. المؤشر مقيد بـ 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 هو ما يعوقه التطبيق (الـ apply.requires في المخطط)؛ missingPrerequisites هو كل ما لا يزال لبنائه قبل أن يعمل التطبيق، بترتيب البناء - الإغلاق الانتقالي لتلك الـ requires، لذا يمكن أن تكون القائمة الأطول. warnings تذكر المشاكل غير المعيقة الخاصة بالتغيير نفسه - اليوم، تغيير جاهز للتنفيذ بدون مواصفات delta وبدون skip_specs: true، ترفضه حالة openspec validate. كلا الحقلين الاختياريين للجذر (context، operationGuidance) يُقرأان من الجذر المحدد في كل استدعاء. context هو إدخال مطلوب على مستوى المطالبة يجب تطبيق حقائق المشروع ذات الصلة، والاتفاقيات، والقيود عليه؛ operationGuidance هو إدخال استشاري يتم اتباع إدخالاته فقط عندما تكون قابلة للتطبيق ومتوافقة مع سير العمل المدمج. تبقى منفصلة عن الحالة، والمهام، والتقدم، وملفات السياق، والتعليمات المدمجة.
4.7 instructions archive --json
{ "changeName", "context"?, "operationGuidance"?, "root" }. يتطلب --change صالحاً في جذر المستودع/المتجر المحلول ويستخدم نفس دلالات السياق المطلوب/الإرشاد الاستشاري كما في التطبيق. هذا سطح إدخال وقت تشغيل للقراءة فقط: لا يعيد سير عمل الأرشيف الثابت، أو يفحص أو يدمج مواصفات delta، أو يكتب المواصفات الرئيسية، أو ينقل التغيير.
4.8 new change <name> --json
نجاح: { "change": { "id", "path", "metadataPath", "schema" }, "root" }. فشل: { "change": null, "status": [d] }، يخرج بـ 1.
4.9 archive <name> --json
نجاح: { "archive": { "change", "archivedAs": "YYYY-MM-DD-name", "path", "specsUpdated", "totals"?, "warnings"? }, "root" }. فشل: { "archive": null, "root"?, "status": [d] }، يخرج بـ 1. تكون specsUpdated صحيحة فقط عندما تمت كتابة أو تقاعد ملف مواصفة واحد على الأقل (يتم حذف مواصفة القدرة التي أزال متطلباتها الأخيرة التغيير، وهو ما يتطلب retire_capabilities: true في .openspec.yaml الخاص بالتغيير؛ يتم تسمية كل تقاعد في warnings، مع أمر استرداد Git قابلاً للصق فقط عندما عاشت المواصفة في فحص المتصل)؛ يؤرشف التغيير المتزامن بالفعل بإجماليات صفرية والسجلات المدرجة في warnings. وضع JSON غير تفاعلي تماماً: يصبح كل نقطة مطالبة رمز 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 (موجود فقط لفحص متجر مدعوم بـ git لديه مرجع تتبع علوي) هو عددها أمام/خلف مقابل آخر جلب علوي، وليس الخلفية الحية. نتائج الصحة بأي شدة تخرج بـ 0. حمولة الفشل: { "root": null, "store": null, "references": [], "status": [d] }، يخرج بـ 1.
4.11 context --json
{ "root": { "path", "source", "store_id"?, "role": "openspec_root" }, "members": [ { "role": "referenced_store", "id", "path"?, "remote"?, "fetch"?, "status": [] } ], "status": [] }. متاح = المسار موجود وحالة فارغة. --code-workspace <path> يكتب {folders:[{name,path}]} (فقط المتاجر المرجعية المتاحة، بادئات ref:)؛ في وضع JSON، يعمل الكتابة قبل الطباعة بحيث يحمل stdout مستنداً واحداً بالضبط حتى على فشل الكتابة. فشل: { "root": null, "members": [], "status": [d] }، يخرج بـ 1.
4.12 store ... --json
إعداد/تسجيل: { "store": {id, root, metadata_path?}, "registry": {path, registered, already_registered}, "git": {is_repository, initialized, committed}, "created_files": [], "status": [] }. إلغاء تسجيل/إزالة: { "store", "registry": {path, removed}, "files": {deleted, deleted_path, left_on_disk}, "status": [] }. قائمة: { "stores": [{id, root}], "status": [] }. طبيب: { "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 = مجهول/لم يتم فحصه). تخرج نتائج الصحة بـ 0؛ تخرج الفشل بـ 1 مع الشكل الخالي المطابق. خروج المطالبة بـ 130.
4.13 schemas --json / templates --json
schemas: يبقى النجاح كمصفوفة عارية [ {name, description, artifacts, source} ]؛ يحلل أولوية اختيار الجذر القياسية ويقبل --store <id>. فشل اختيار الجذر: { "schemas": [], "root": null, "status": [d] }، يخرج بـ 1. templates: كائن مفتاحي { "<artifactId>": {path, source} }، لا يزال قائماً على cwd بدون مفاتيح جذر/حالة.
5. عقد رمز الخروج
| الحالة | رمز الخروج | الإخراج القياسي |
|---|---|---|
| نجاح، بما في ذلك نتائج الفحص (doctor/context/store doctor) | 0 | الحمولة |
فشل الأمر في وضع --json | 1 | مستند JSON واحد يحتوي على status: [d] وشكل القيمة الفارغة للأمر |
validate مع عناصر فاشلة | 1 | التقرير الكامل |
إلغاء المُوجِّه (store group، الوضع البشري) | 130 | الإخراج القياسي للخطأ فقط |
6. كتالوج رموز التشخيص
الحل
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؛ المرور المباشر: invalid_store_id، invalid_store_registry، invalid_store_metadata.
صحة جذر OpenSpec (خطأ، لا إصلاح)
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. خلال فترة تجريب المتاجر (stores beta)، قد تغيب openspec/specs/ وopenspec/changes/ وopenspec/changes/archive/ في جذر سليم؛ وهي تُعد أخطاء صحة فقط عند وجودها دون أن تكون مجلدات.
سجل المتاجر/الهوية/الحالة
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 (معلومة).
إعداد/تسجيل/إزالة المتاجر
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 (تحذير في الإزالة، خطأ في doctor)، store_root_not_directory.
Git للمتاجر
store_git_init_failed، store_git_identity_missing، store_git_commit_failed، store_git_no_commits (تحذير)، store_clone_fragile_directories (تحذير)، store_remote_divergence (معلومة، doctor)، store_checkout_drift (معلومة، doctor).
المراجع (تحذير)
reference_invalid_id، reference_registry_unreadable، reference_unresolved، reference_root_unhealthy، reference_index_truncated.
العلاقات (تحذير؛ doctor؛ context يحتفظ بسجل العلاقات فقط)
relationship_registry_unreadable، root_pointer_ignored، root_pointer_invalid، pointer_declarations_inert.
الأرشيف (وضع 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.
عمليات الكتابة في context
context_file_exists، context_output_dir_missing.
البدائل الاحتياطية
doctor_failed، context_failed، store_error، change_error، archive_error.
عدم اتساقات معروفة
سُجّلت من خلال تدقيق المشروع الختامي؛ إعادة تسمية المفاتيح المنشورة هي قرارات منتج مؤجّلة لما بعد هذا الإصدار:
في وضعتم إصلاحه في جولة الاختبار الشاملة للمشروع الختامي: العناصر المجهولة وغير الواضحة في--json، كانت مسارات فشل متعددة تُطبع الإخراج القياسي للخطأ فقط دون مستند JSON.show/validateتُصدر{status:[{code: unknown_item | ambiguous_item, ...}]}؛ الأخطاء المستثناة فيstatus/instructions/list/show/validateتمر عبر مساعد الفشل الواعي بـ JSON (شكل القيمة الفارغة للأمر +status)؛store <unknown subcommand> --jsonيُصدر{status:[{code: unknown_store_subcommand}]}؛listيحمل شكل القيمة الفارغة{changes|specs: [], root: null}عند فشل الحل.store_root_missingيُصدر بمستويين من الخطورة (تحذير في الإزالة، خطأ في store doctor) — يعتمد على السياق، موثّق أعلاه.- تباين تسمية المفاتيح بين snake_case (عائلة المتاجر) وcamelCase (عائلة سير العمل)؛
root.store_idهو snake_case في كل مكان. - توجد أربع إعلانات موازٍ لنوع الغلاف (envelope) في src؛ تشخيصات الأرشيف لا تحمل أبدًا
target. list --jsonيعيد استخدام مفتاحstatusكـ enum نصي لكل تغيير.- فقط مخرجات
validateتحمل حقلversion. templatesيتجاهل اختيار الجذر (يعتمد على cwd، لا--store).- الأشكال الإسمية المهجورة (أوامر
change/specالفرعية) تُصدر حمولات غير مغلفة دونroot/status.