@clawplays/ospec-cli
Advanced tools
@@ -36,3 +36,3 @@ --- | ||
| - عند تنفيذ classic change، لا تنشئ goal-only files ما لم يطلب المستخدم ترقية العمل صراحة إلى goal | ||
| - `Announce-Before-Act`: لا تُشغّل سير العمل بصمت. أعلن OSpec skill والمرحلة، والأمر والـ artifact، وruntime adapter المختار، وعدد workers، والآلية الفعلية، وأي بوابة تحجب التقدم | ||
| - `Announce-Before-Act`: لا تُشغّل سير العمل بصمت. أعلن OSpec skill والمرحلة، والأمر والـ artifact، وmodel-native subagent adapter المختار، وعدد workers، وcurrent session capability، وأي بوابة تحجب التقدم | ||
| - `Brainstorm-First`: ابدأ كل goal بجولة عصف ذهني قصيرة قبل تثبيت التصميم. اعرض الأسئلة المفتوحة حول الاتجاه والبنية وAPI والبيانات وUI والمخاطر والنطاق، واسأل المستخدم سؤالاً واحداً في كل مرة بدلاً من الافتراض الصامت؛ واحفظ الاستكشاف عبر `ospec brainstorm [path] --topic "..."` عند الحاجة. وعندما يكون أي منها مفتوحاً فعلاً، فضّل رفع decision gate دائمة على التخمين؛ ولا تسجّل افتراضاً ذاتياً في `design.md` إلا عندما يفوّض المستخدم صراحةً أو يكون غير متاح، مع وسمه كافتراض بحاجة لتأكيد | ||
@@ -54,11 +54,9 @@ - عندما يجب أن ينتظر change اختيار المستخدم، سجّل durable decision gate عبر `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:impact --option id:label:impact [--recommended id] [--required]`، واعرض `Chat Prompt` من decision report أو `artifacts/agents/decisions/index.md`، ثم سجّل الإجابة عبر `ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` | ||
| - توفير الـ tokens (لا يغيّر أي خطوة): مرّر `--brief` على `ospec execute …` لقراءة ملخّص مختصر بدل التقرير الكامل، وقُد كل خطوة من `ospec execute status --brief` بدل إعادة قراءة `task-graph.json` أو `worker-status.md` أو `launch-plan.md` الكاملة في كل دورة — تُكتب الـ artifacts كاملةً على القرص، فافتحها فقط عند الحاجة للتفاصيل | ||
| - بعد dispatch، استخدم `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` لكتابة launch plan؛ يحل `runtimeAdapter` بالترتيب: Orca ownership موثّق، ثم current harness-native capability، ثم target CLI متاح، ثم serial generic current controller. لا يكفي اسم process لإثبات Orca ownership | ||
| - يتبع multi-worker execution القيمة `runtimeAdapter.selected`، ولا يعمل بالتوازي إلا عندما يدعم adapter المختار ذلك. يمكن للـ task العادي الرجوع إلى serial current controller، أما independent review فيتطلب independent adapter | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: المستوى L1 للتقارير فقط. في L2/L3 يملك IDE AI دورة tick -> تنفيذ كل `actions[]` عبر selected runtime adapter لكل action -> تسجيل heartbeat/result evidence -> tick فوري. إذا كانت `actions[]` فارغة مع وجود `pending` فهذه مراقبة فقط ولا يعاد التوزيع | ||
| - استخدم `ospec execute orchestrate [changes/active/<change>] --command "..."` فقط عندما يختار selected adapter أو fallback order المسجل مسار explicit CLI orchestration | ||
| - استخدم `--run --command` عندما يحتاج selected target-CLI adapter إلى single-worker runner أو يفشل adapter سابق قبل claim ownership، وسجل `artifacts/agents/worker-runs/`؛ ثم استخدم `ospec execute collect ...` لتسجيل النتيجة، واستخدم `ospec execute retry` لإعادة dispatch عند الحاجة | ||
| - لا يشغّل OSpec local reviewer command إلا عند استخدام `ospec execute review ... --run --command "..."` صراحة؛ يسجل ذلك `artifacts/agents/review-runs/` ويمكنه كتابة review decision عند تمرير `--decision` | ||
| - بعد dispatch، استخدم `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` لكتابة launch plan؛ يقبل `runtimeAdapter` فقط model-native subagent capability حالية ومرتبطة بالـ target ويعرض native primitive | ||
| - نفّذ `runtimeAdapter.selected.nativeSubagent` وشغّل safe batch فقط بالتوازي. عند غياب capability أو انتهاء صلاحيتها يجب block من دون agent CLI أو fallback إلى current controller | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: المستوى L1 للتقارير فقط. في L2/L3 يملك IDE AI دورة tick -> تنفيذ كل `actions[]` عبر `runtimeAdapter.selected.nativeSubagent` لكل action -> تسجيل heartbeat/result evidence -> tick فوري. إذا كانت `actions[]` فارغة مع وجود `pending` فهذه مراقبة فقط ولا يعاد التوزيع | ||
| - أزيل agent CLI execution. تفشل `execute orchestrate` و`launch --run --command` و`review --run --command` و`loop watch` قبل تشغيل process أو إنشاء run artifact. استخدم `ospec execute retry` لإعادة native work | ||
| - عندما يكون debugging جزءا من change، استخدم `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` لتسجيل root-cause وfix evidence؛ هذا الأمر يسجل evidence فقط ولا يشغّل أوامر shell | ||
| - بعد تشغيل focused tests، استخدم `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` لتسجيل TDD cycle evidence؛ هذا الأمر يسجل evidence فقط ولا يشغّل أوامر shell | ||
| - بعد تشغيل project checks حديثة، استخدم `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` لتسجيل verification evidence؛ هذا الأمر يسجل evidence فقط ولا يشغّل أوامر shell | ||
| - بعد تشغيل project checks حديثة، استخدم `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` لتسجيل verification evidence؛ هذا الأمر يسجل evidence فقط ولا يشغّل أوامر shell | ||
| - `ospec execute doc-review` يسجل artifacts فقط ولا يشغّل reviewers ولا أوامر shell ولا يزامن worker status ولا يعدّل source files | ||
@@ -65,0 +63,0 @@ - في goal لا تؤرشف عندما يحتوي `artifacts/agents/task-graph.json` على حالات مهام غير محسومة أو اعتماديات غير صالحة أو ملفات مستهدفة ناقصة أو أوامر تحقق ناقصة أو عندما لا يكون `status` العلوي `completed` |
@@ -33,3 +33,3 @@ --- | ||
| - أثناء تنفيذ goal بمساعدة AI، أنشئ `design.md` أو حدّثه بعد `proposal.md` وقبل تعديل `implementation-plan.md` أو `tasks.md` أو الكود. في classic change لا تنشئ goal-only files ما لم يطلب المستخدم الترقية إلى goal صراحة | ||
| - `Announce-Before-Act`: لا تُشغّل سير العمل بصمت. أعلن OSpec skill والمرحلة، والأمر والـ artifact، وruntime adapter المختار، وعدد workers، والآلية الفعلية، وأي بوابة تحجب التقدم | ||
| - `Announce-Before-Act`: لا تُشغّل سير العمل بصمت. أعلن OSpec skill والمرحلة، والأمر والـ artifact، وmodel-native subagent adapter المختار، وعدد workers، وcurrent session capability، وأي بوابة تحجب التقدم | ||
| - `Brainstorm-First`: ابدأ كل goal بجولة عصف ذهني قصيرة قبل تثبيت التصميم. اعرض الأسئلة المفتوحة حول الاتجاه والبنية وAPI والبيانات وUI والمخاطر والنطاق، واسأل المستخدم سؤالاً واحداً في كل مرة بدلاً من الافتراض الصامت؛ واحفظ الاستكشاف عبر `ospec brainstorm [path] --topic "..."` عند الحاجة. وعندما يكون أي منها مفتوحاً فعلاً، فضّل رفع decision gate دائمة على التخمين؛ ولا تسجّل افتراضاً ذاتياً في `design.md` إلا عندما يفوّض المستخدم صراحةً أو يكون غير متاح، مع وسمه كافتراض بحاجة لتأكيد | ||
@@ -54,15 +54,13 @@ - عندما يجب أن ينتظر change اختيار المستخدم، سجّل durable decision gate عبر `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:impact --option id:label:impact [--recommended id] [--required]`، واعرض `Chat Prompt` من decision report أو `artifacts/agents/decisions/index.md`، ثم سجّل الإجابة عبر `ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` | ||
| - توفير الـ tokens (لا يغيّر أي خطوة): مرّر `--brief` على `ospec execute …` لقراءة ملخّص مختصر بدل التقرير الكامل، وقُد كل خطوة من `ospec execute status --brief` بدل إعادة قراءة `task-graph.json` أو `worker-status.md` أو `launch-plan.md` الكاملة في كل دورة — تُكتب الـ artifacts كاملةً على القرص، فافتحها فقط عند الحاجة للتفاصيل | ||
| - بعد dispatch، استخدم `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` لكتابة launch plan؛ يحل `runtimeAdapter` بالترتيب: Orca ownership موثّق، ثم current harness-native capability، ثم target CLI متاح، ثم serial generic current controller. لا يكفي اسم process لإثبات Orca ownership. ينشئ single-task dispatch هذه الخطة تلقائيا | ||
| - يتبع multi-worker execution القيمة `runtimeAdapter.selected`، ولا يعمل بالتوازي إلا عندما يدعم adapter المختار ذلك. يمكن للـ task العادي الرجوع إلى serial current controller، أما independent review فيتطلب independent adapter | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: المستوى L1 للتقارير فقط. في L2/L3 يملك IDE AI دورة tick -> تنفيذ كل `actions[]` عبر selected runtime adapter لكل action -> تسجيل heartbeat/result evidence -> tick فوري. إذا كانت `actions[]` فارغة مع وجود `pending` فهذه مراقبة فقط ولا يعاد التوزيع | ||
| - استخدم `ospec execute orchestrate [changes/active/<change>] --command "..."` فقط عندما يختار selected adapter أو fallback order المسجل مسار explicit CLI orchestration | ||
| - استخدم `--run --command` عندما يحتاج selected target-CLI adapter إلى single-worker runner أو يفشل adapter سابق قبل claim ownership، وسجل `artifacts/agents/worker-runs/`؛ ثم استخدم `ospec execute collect ...` لتسجيل النتيجة، واستخدم `ospec execute retry` لإعادة dispatch عند الحاجة | ||
| - بعد dispatch، استخدم `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` لكتابة launch plan؛ يقبل `runtimeAdapter` فقط model-native subagent capability حالية ومرتبطة بالـ target. ينشئ single-task dispatch هذه الخطة تلقائيا | ||
| - نفّذ `runtimeAdapter.selected.nativeSubagent` وشغّل safe batch فقط بالتوازي. عند غياب capability أو انتهاء صلاحيتها يجب block من دون agent CLI أو fallback إلى current controller | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: المستوى L1 للتقارير فقط. في L2/L3 يملك IDE AI دورة tick -> تنفيذ كل `actions[]` عبر `runtimeAdapter.selected.nativeSubagent` لكل action -> تسجيل heartbeat/result evidence -> tick فوري. إذا كانت `actions[]` فارغة مع وجود `pending` فهذه مراقبة فقط ولا يعاد التوزيع | ||
| - أزيل agent CLI execution. تفشل `execute orchestrate` و`launch --run --command` و`review --run --command` و`loop watch` قبل تشغيل process أو إنشاء run artifact. استخدم `ospec execute retry` لإعادة native work | ||
| - بعد اكتمال كل worker task، استخدم `ospec execute review [changes/active/<change>] --task <task-id>` لإنشاء مراجعة موحدة وملف `artifacts/agents/review-packages/*.diff` محدود بنطاق المهمة. يقرأ reviewer الحزمة مرة واحدة، وتُسجل الأحجام والمدة في `artifacts/agents/execution-metrics.json` | ||
| - عند تفعيل `documentation_updates` في task graph، يجب أن تحتوي كل مهمة على المصفوفة (`[]` عند عدم الحاجة)، وأن يظهر كل مسار docs معلن أيضا في `target_files` للمهمة نفسها، وأن يكون موجودا قبل الأرشفة، وأن يملك دليلا على تغير محتوى فعلي بين dispatch وcomplete. تبقى التشغيلات القديمة بلا baseline متوافقة لكنها تعرض الدليل كغير متحقق. | ||
| - بعد اعتماد كل task-level reviews واكتمال task graph، استخدم `ospec execute review [changes/active/<change>]` من دون `--task` لإنشاء حزمة code review نهائية موحدة واحدة داخل `artifacts/agents/review-dispatches/`؛ وهي مراجعة واحدة تكتب قرار `artifacts/reviews/final-review.md` واحد | ||
| - لا يشغّل OSpec local reviewer command إلا عند استخدام `ospec execute review ... --run --command "..."` صراحة؛ يسجل ذلك `artifacts/agents/review-runs/` ويمكنه كتابة review decision عند تمرير `--decision` | ||
| - بعد أن يحتوي review artifact على قرار غير `PENDING`، استخدم `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` لكتابة `artifacts/agents/review-feedback-plan.json` و`artifacts/agents/review-feedback-plan.md`؛ حدد accept أو revise أو clarify أو blocked قبل dispatch عمل إضافي، وأنشئ required user decision عندما يغير feedback scope أو direction أو API أو UI أو risk أو accepted tradeoffs، وأنشئ required user decision gate عندما يغير feedback scope أو direction أو API أو UI أو risk أو accepted tradeoffs | ||
| - عندما يكون debugging جزءا من change، استخدم `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` لتسجيل `artifacts/agents/debug-evidence.json`؛ تعني `CONFIRMED` عزل root cause، وتعني `FIXED` إصلاحا متحققا، وتؤدي `BLOCKED` إلى فشل verify | ||
| - بعد تشغيل focused tests، استخدم `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` لتسجيل `artifacts/agents/tdd-evidence.json`؛ عادة يسجل red الاختبار المتوقع فشله، بينما يجب أن تسجل green/refactor نتيجة ناجحة | ||
| - بعد تشغيل project verification commands حديثة، استخدم `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` لتسجيل `artifacts/agents/verification-evidence.json`؛ لا تدّعِ الاكتمال بمجرد ملخص داخل المحادثة | ||
| - بعد تشغيل project verification commands حديثة، استخدم `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` لتسجيل `artifacts/agents/verification-evidence.json`؛ لا تدّعِ الاكتمال بمجرد ملخص داخل المحادثة | ||
| - بعد تعديل task graph أو execution session أو review artifacts أو debug evidence أو verification checklist يدويا، استخدم `ospec execute sync [changes/active/<change>]` لإعادة بناء `artifacts/agents/worker-status.md` | ||
@@ -69,0 +67,0 @@ - في goal يجب اشتقاق `tasks.md` من `artifacts/agents/task-graph.json`؛ وإذا كانت المهام موجودة لكن وثائقها السابقة ما زالت قوالب، فحدّث الوثائق السابقة أولاً ثم وائم المهام معها. في classic change تُشتق `tasks.md` مباشرة من `proposal.md` ونطاق التنفيذ |
@@ -37,3 +37,3 @@ --- | ||
| - In classic change execution, do not create goal-only files unless the user explicitly upgrades the work to a goal. | ||
| - `Announce-Before-Act`: never run the workflow silently. Announce the OSpec skill and stage, command and artifact, selected runtime adapter, worker count, actual mechanism, and any blocking gate | ||
| - `Announce-Before-Act`: never run the workflow silently. Announce the OSpec skill and stage, command and artifact, selected model-native subagent adapter, worker count, current session capability, and any blocking gate | ||
| - `Brainstorm-First`: open each goal with a short brainstorming pass before locking design. Surface the open questions for direction, architecture, API, data, UI, risk, and scope, and ask the user one question at a time instead of silently assuming; persist exploration with `ospec brainstorm [path] --topic "..."` when useful. When any of those is genuinely open, prefer raising a durable decision gate over guessing rather than writing a silent assumption; only record an autonomous assumption in `design.md` when the user explicitly defers or is unavailable, and label it as an assumption to confirm | ||
@@ -55,16 +55,14 @@ - When the change must pause for a user choice, record a durable gate with `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:impact --option id:label:impact [--recommended id] [--required]`, present the decision report `Chat Prompt` or `artifacts/agents/decisions/index.md`, then record the selected option with `ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` | ||
| - Use `ospec execute dispatch` to create a parallel-safe batch of worker packets and `ospec execute complete` to record worker results when task-level execution needs durable handoff artifacts; each dispatch packet includes the project session brief snapshot and a worker profile with capability tier, recommended target, target tool mapping, rationale, and required behavior; required pending user decisions block dispatch; `complete` writes `artifacts/agents/blockers/` when the result is `NEEDS_CONTEXT` or `BLOCKED`; use `--task` for one explicit task and `--limit` to cap dispatch batch size; after each `DONE` or `DONE_WITH_CONCERNS` worker result, run the task's single combined review with `ospec execute review [changes/active/<change>] --task <task-id>` (spec compliance and code quality in one pass) before dispatching dependent work; after all task-level reviews complete, use `ospec execute review` without `--task` for the single combined final whole-change code review packet; use `ospec execute feedback` after non-`PENDING` final review decisions to write `artifacts/agents/review-feedback-plan.md`; use `ospec execute sync` to rebuild `worker-status.md` after manual execution or review artifact edits | ||
| - Use `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` after dispatch to write the launch plan. Its `runtimeAdapter` resolves verified Orca ownership, current harness-native capability, an available target CLI, then the serial generic current controller. A process name alone never proves Orca ownership | ||
| - Multi-worker execution follows `runtimeAdapter.selected`; parallelize only when the selected adapter supports it. Ordinary work may fall back to the serial current controller, while independent review requires an independent adapter | ||
| - For an integrated goal loop in controller mode, do not stop after initialization or ask the user to run Loop commands. Run `ospec loop run [change] --once --json`, execute every emitted action through its `runtimeAdapter.selected`, persist heartbeat/result evidence, and tick again without another user prompt; do not paste the whole goal into each worker | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 is report-only. For L2/L3, the IDE AI owns tick -> execute all `actions[]` through each action's selected runtime adapter -> persist heartbeat/result evidence -> immediate tick. An empty `actions[]` with `pending` is observation only and must never be relaunched | ||
| - In CLI-driven mode, `ospec loop watch` is the session-bound executor for fresh `claude -p` / `codex exec` processes. It runs ready action batches in parallel, re-observes evidence immediately, waits only when no action is ready, and stops on pause/STOP/completion/budgets/no-progress/comprehension guards or process exit; use controller mode for unsupported direct targets | ||
| - Use `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` after dispatch to write the launch plan. Its `runtimeAdapter` accepts only a current, target-bound model-native subagent capability and exposes the native primitive | ||
| - Execute `runtimeAdapter.selected.nativeSubagent`; parallelize only the safe batch. If capability is missing or expired, block and refresh the current model session instead of starting an agent CLI or using the controller context | ||
| - For an integrated goal loop in controller mode, do not stop after initialization or ask the user to run Loop commands. Run `ospec loop run [change] --once --json`, execute every emitted action through its `runtimeAdapter.selected.nativeSubagent`, persist heartbeat/result evidence, and tick again without another user prompt; do not paste the whole goal into each worker | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 is report-only. For L2/L3, the IDE AI owns tick -> execute all `actions[]` through each action's `runtimeAdapter.selected.nativeSubagent` -> persist heartbeat/result evidence -> immediate tick. An empty `actions[]` with `pending` is observation only and must never be relaunched | ||
| - Agent CLI execution is removed. `loop watch`, `execute orchestrate`, `launch --run --command`, and `review --run --command` fail before process launch or run-artifact creation | ||
| - Required decisions block every loop level. L1 emits no executable action; L3 additionally requires non-empty path and command allowlists and blocks targets or verification commands outside them | ||
| - Token economy (does not change any gate): pass `--brief` on `ospec execute …`, drive each step from `ospec execute status --brief`, and consume the packet paths emitted by the loop instead of re-reading or embedding the full `task-graph.json`, `worker-status.md`, `launch-plan.md`, or goal documents — full artifacts stay on disk for on-demand detail | ||
| - Use `ospec execute orchestrate [changes/active/<change>] --command "..."` only when the selected adapter or recorded fallback order chooses explicit CLI orchestration; run only the adapter-supported batch | ||
| - Use explicit `--run --command` when the selected target-CLI adapter needs a single-worker runner or an earlier adapter fails before claiming ownership. Then use `ospec execute collect ...` to record the result. Use `ospec execute retry` to reopen corrected blocked, needs-context, or failed work; completed tasks require explicit `--force` | ||
| - Use explicit `ospec execute review ... --run --command "..."` only when OSpec should run a local reviewer command; it captures `artifacts/agents/review-runs/` and can write the task-level or final review decision when `--decision` is provided | ||
| - Use `ospec execute retry` to reopen corrected blocked, needs-context, or failed native work; completed tasks require explicit `--force` | ||
| - When debugging is part of the change, use `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` to record root-cause and fix evidence; it records evidence only and does not run shell commands | ||
| - After focused test runs, use `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` to record TDD cycle evidence; it records evidence only and does not run shell commands | ||
| - After running fresh project checks, use `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` to record verification evidence; it records evidence only and does not run shell commands | ||
| - `ospec execute doc-review` records artifacts only and does not launch reviewers or edit source files. Execute specialist reviews through the recorded independent runtime adapter and bind the real executor id with claim/heartbeat/complete commands | ||
| - After running fresh project checks, use `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` to record verification evidence; it records evidence only and does not run shell commands | ||
| - `ospec execute doc-review` records artifacts only and does not launch reviewers or edit source files. Execute specialist reviews through `runtimeAdapter.selected.nativeSubagent` and bind the real child id with claim/heartbeat/complete commands | ||
| - For goal execution, do not archive while `artifacts/agents/task-graph.json` has unresolved task statuses, invalid dependencies, missing target files, missing verification commands, or top-level `status` other than `completed` | ||
@@ -71,0 +69,0 @@ - After implementation, each task's single combined review must complete, and the single final `artifacts/reviews/final-review.md` must complete; unresolved task-level or final review decisions block archive |
@@ -35,3 +35,3 @@ --- | ||
| - During AI-assisted goal execution, draft or update `design.md` after `proposal.md` and before editing `implementation-plan.md`, `tasks.md`, or code. Do not create goal-only files during a classic change unless the user explicitly upgrades it to a goal. | ||
| - `Announce-Before-Act`: never run the workflow silently. Announce the OSpec skill and stage, command and artifact, selected runtime adapter, worker count, actual mechanism, and any blocking gate | ||
| - `Announce-Before-Act`: never run the workflow silently. Announce the OSpec skill and stage, command and artifact, selected model-native subagent adapter, worker count, current session capability, and any blocking gate | ||
| - `Brainstorm-First`: open each goal with a short brainstorming pass before locking design. Surface the open questions for direction, architecture, API, data, UI, risk, and scope, and ask the user one question at a time instead of silently assuming; persist exploration with `ospec brainstorm [path] --topic "..."` when useful. When any of those is genuinely open, prefer raising a durable decision gate over guessing rather than recording a silent assumption; only record an autonomous assumption in `design.md` when the user explicitly defers or is unavailable, and label it as an assumption to confirm | ||
@@ -55,15 +55,13 @@ - When the change must pause for a user choice, record a durable gate with `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:impact --option id:label:impact [--recommended id] [--required]`, present the decision report `Chat Prompt` or `artifacts/agents/decisions/index.md`, then record the selected option with `ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` | ||
| - Use `ospec execute dispatch [changes/active/<change>] [--task task-id] [--limit N]` to create a parallel-safe batch of worker packets and `artifacts/agents/execution-session.json`; each packet includes the project session brief snapshot and a worker profile with capability tier, recommended target, target tool mapping, rationale, and required behavior. Use `ospec execute complete <task-id> ...` to record worker results. Use `--task` for one explicit task and `--limit` to cap dispatch batch size. These commands also sync `artifacts/agents/worker-status.md`, update OSpec artifacts only, and do not launch external workers; required pending user decisions block dispatch; `complete` writes blocker escalation artifacts under `artifacts/agents/blockers/` when the result is `NEEDS_CONTEXT` or `BLOCKED` | ||
| - Use `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` after dispatch to write the launch plan. Its `runtimeAdapter` resolves verified Orca ownership, current harness-native capability, an available target CLI, then the serial generic current controller. A process name alone never proves Orca ownership. A single-task dispatch generates this plan automatically | ||
| - Multi-worker execution follows `runtimeAdapter.selected`; parallelize only when the selected adapter supports it. Ordinary work may fall back to the serial current controller, while independent review requires an independent adapter | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 is report-only. For L2/L3, the IDE AI owns tick -> execute all `actions[]` through each action's selected runtime adapter -> persist heartbeat/result evidence -> immediate tick. An empty `actions[]` with `pending` is observation only and must never be relaunched | ||
| - Use `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` after dispatch to write the launch plan. Its `runtimeAdapter` accepts only a current, target-bound model-native subagent capability. A single-task dispatch generates this plan automatically | ||
| - Execute `runtimeAdapter.selected.nativeSubagent`; parallelize only the safe batch. Missing or expired native capability blocks dispatch with no agent CLI or controller-context fallback | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 is report-only. For L2/L3, the IDE AI owns tick -> execute all `actions[]` through each action's `runtimeAdapter.selected.nativeSubagent` -> persist heartbeat/result evidence -> immediate tick. An empty `actions[]` with `pending` is observation only and must never be relaunched | ||
| - Token economy (does not change any step): pass `--brief` on `ospec execute …` to read a lean summary instead of the full report, and drive each step from `ospec execute status --brief` instead of re-reading the full `task-graph.json`, `worker-status.md`, or `launch-plan.md` every turn — the artifacts are still written in full, so open them only when you need the detail | ||
| - Use `ospec execute orchestrate [changes/active/<change>] --command "..."` only when the selected adapter or recorded fallback order chooses explicit CLI orchestration; run only the adapter-supported batch | ||
| - Use explicit `--run --command` when the selected target-CLI adapter needs a single-worker runner or an earlier adapter fails before claiming ownership; runs write `artifacts/agents/worker-runs/`. Then use `ospec execute collect ...` to record the result. Use `ospec execute retry` to reopen corrected blocked, needs-context, or failed work; completed tasks require explicit `--force` | ||
| - Agent CLI execution is removed: `execute orchestrate`, `launch --run --command`, `review --run --command`, and `loop watch` fail before process launch or run-artifact creation. Use `ospec execute retry` for corrected native work; completed tasks require explicit `--force` | ||
| - After a worker records `DONE` or `DONE_WITH_CONCERNS`, use `ospec execute review [changes/active/<change>] --task <task-id>` to create one combined code reviewer packet and a scoped `artifacts/agents/review-packages/*.diff`; read that package once instead of repeating broad Git discovery. Packet, report, package, and duration metrics are recorded in `artifacts/agents/execution-metrics.json` | ||
| - After all task-level reviews are approved and the task graph is completed, use `ospec execute review [changes/active/<change>]` without `--task` to create one combined final whole-change code reviewer packet with the project session brief snapshot under `artifacts/agents/review-dispatches/`; it is a single review that writes one `artifacts/reviews/final-review.md` decision | ||
| - Use explicit `ospec execute review ... --run --command "..."` only when OSpec should run a local reviewer command; it captures `artifacts/agents/review-runs/` and can write the task-level or final review decision when `--decision` is provided | ||
| - Use `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` after a review artifact has a non-`PENDING` decision to write `artifacts/agents/review-feedback-plan.json` and `artifacts/agents/review-feedback-plan.md`; handle review feedback through accept, revise, clarify, or blocked actions before dispatching more work, and create a required user decision gate when feedback changes scope, direction, API, UI, risk, or accepted tradeoffs | ||
| - When debugging is part of the change, use `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` to record `artifacts/agents/debug-evidence.json`; `CONFIRMED` isolates root cause, `FIXED` verifies the fix, and `BLOCKED` fails verification | ||
| - After focused test runs, use `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` to record `artifacts/agents/tdd-evidence.json`; red must record a non-passing focused test before implementation, green requires a prior red `FAILED` record, refactor requires prior passing green/refactor evidence, and `SKIPPED` requires a concrete summary | ||
| - After running fresh project verification commands, use `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` to record `artifacts/agents/verification-evidence.json`; do not claim completion with only an unrecorded chat summary | ||
| - After running fresh project verification commands, use `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` to record `artifacts/agents/verification-evidence.json`; do not claim completion with only an unrecorded chat summary | ||
| - Use `ospec execute sync [changes/active/<change>]` after manual task graph, execution-session, review artifact, debug evidence, or verification checklist edits to rebuild `artifacts/agents/worker-status.md` | ||
@@ -70,0 +68,0 @@ - For goals, derive `tasks.md` from `artifacts/agents/task-graph.json`; if tasks already exist but upstream docs are still templates, update them first and then reconcile tasks. For classic changes, derive `tasks.md` directly from `proposal.md` and the implementation scope. |
@@ -36,3 +36,3 @@ --- | ||
| - classic change では、ユーザーが明示的に goal へ昇格させない限り goal-only files を作成しない | ||
| - `Announce-Before-Act`: ワークフローを黙って実行しない。OSpec skill・段階、コマンドと生成物、選択された runtime adapter、worker 数、実際の機構、blocking gate を伝える | ||
| - `Announce-Before-Act`: ワークフローを黙って実行しない。OSpec skill・段階、コマンドと生成物、選択された model-native subagent adapter、worker 数、current session capability、blocking gate を伝える | ||
| - `Brainstorm-First`: 各 goal は設計を確定する前に短いブレインストーミングから始める。方向、アーキテクチャ、API、データ、UI、リスク、スコープの未決事項を 1 つずつユーザーに質問し、黙って仮定しない。必要に応じて `ospec brainstorm [path] --topic "..."` で探索を保存する。いずれかが本当に未決のときは、黙って仮定を書くより durable な decision gate を上げることを優先する。ユーザーが明示的に委任した、または不在のときだけ `design.md` に仮定を記録し、要確認の仮定として明記する | ||
@@ -54,11 +54,9 @@ - change がユーザー選択を待つ必要がある場合は `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:impact --option id:label:impact [--recommended id] [--required]` で durable decision gate を記録し、decision report の `Chat Prompt` または `artifacts/agents/decisions/index.md` を提示してから、`ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` で回答を記録する | ||
| - トークン節約(どの手順も変えない):`ospec execute …` に `--brief` を付けて完全なレポートではなく簡潔な要約を読み、毎ターン `task-graph.json` / `worker-status.md` / `launch-plan.md` 全体を読み直す代わりに `ospec execute status --brief` で各ステップを駆動する——artifact は完全な形でディスクに書かれるので、詳細が必要なときだけ開く | ||
| - dispatch 後は `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` で launch plan を書く。`runtimeAdapter` は検証済み Orca ownership、current harness-native capability、利用可能な target CLI、serial generic current controller の順で解決する。Orca の process 名だけでは ownership の証拠にならない | ||
| - multi-worker execution は `runtimeAdapter.selected` に従い、選択 adapter が parallel work をサポートするときだけ安全な batch を並列起動する。通常 task は serial current controller に fallback できるが、independent review には independent adapter が必要 | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 は report-only。L2/L3 では IDE AI が tick -> 各 action の selected runtime adapter で全 `actions[]` を実行 -> heartbeat/result evidence -> 即時 tick を担当する。`actions[]` が空で `pending` がある場合は観察のみで再 dispatch しない | ||
| - `ospec execute orchestrate [changes/active/<change>] --command "..."` は selected adapter または記録された fallback order が explicit CLI orchestration を選択した場合だけ使う | ||
| - selected target-CLI adapter が single-worker runner を必要とする場合、または前の adapter が ownership claim 前に失敗した場合は `--run --command` を使い、`artifacts/agents/worker-runs/` を記録する。その後 `ospec execute collect ...` で result を記録し、必要なら `ospec execute retry` で再 dispatch する | ||
| - explicit `ospec execute review ... --run --command "..."` の場合のみ local reviewer command を実行し、`artifacts/agents/review-runs/` を記録する。`--decision` がある場合は review decision も書き戻せる | ||
| - dispatch 後は `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` で launch plan を書く。`runtimeAdapter` は current かつ target-bound な model-native subagent capability のみを受け入れ、native primitive を示す | ||
| - `runtimeAdapter.selected.nativeSubagent` を実行し、安全な batch だけを並列 dispatch する。capability がない、または期限切れの場合は block し、agent CLI や current controller に fallback しない | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 は report-only。L2/L3 では IDE AI が tick -> 各 action の `runtimeAdapter.selected.nativeSubagent` で全 `actions[]` を実行 -> heartbeat/result evidence -> 即時 tick を担当する。`actions[]` が空で `pending` がある場合は観察のみで再 dispatch しない | ||
| - agent CLI execution は削除された。`execute orchestrate`、`launch --run --command`、`review --run --command`、`loop watch` は process 起動や run artifact 作成の前に失敗する。native work の再実行には `ospec execute retry` を使う | ||
| - debugging が change の一部だった場合、`ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` で root cause と fix evidence を記録する。このコマンドは evidence の記録のみを行い、shell command は実行しない | ||
| - focused test 実行後は、`ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` で TDD cycle evidence を記録する。このコマンドは evidence の記録のみを行い、shell command は実行しない | ||
| - fresh project checks を実行した後、`ospec execute verify [changes/active/<change>] --command "..." --status PASSED` で verification evidence を記録する。このコマンドは evidence の記録のみを行い、shell command は実行しない | ||
| - fresh project checks を実行した後、`ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` で verification evidence を記録する。このコマンドは evidence の記録のみを行い、shell command は実行しない | ||
| - `ospec execute doc-review` は artifacts のみを記録し、reviewer 起動、shell command 実行、worker status 同期、source file 編集は行わない | ||
@@ -65,0 +63,0 @@ - goal では `artifacts/agents/task-graph.json` に未解決の task 状態、無効な依存関係、対象ファイル不足、検証コマンド不足、またはトップレベル `status` が `completed` でない状態がある場合は archive しない |
@@ -33,3 +33,3 @@ --- | ||
| - AI 支援で goal を進める場合は、`proposal.md` の後、`implementation-plan.md`、`tasks.md`、コードを編集する前に `design.md` を作成または更新する。classic change では、ユーザーが明示的に goal へ昇格させない限り goal-only files を作成しない | ||
| - `Announce-Before-Act`: ワークフローを黙って実行しない。OSpec skill・段階、コマンドと生成物、選択された runtime adapter、worker 数、実際の機構、blocking gate を伝える | ||
| - `Announce-Before-Act`: ワークフローを黙って実行しない。OSpec skill・段階、コマンドと生成物、選択された model-native subagent adapter、worker 数、current session capability、blocking gate を伝える | ||
| - `Brainstorm-First`: 各 goal は設計を確定する前に短いブレインストーミングから始める。方向、アーキテクチャ、API、データ、UI、リスク、スコープの未決事項を 1 つずつユーザーに質問し、黙って仮定しない。必要に応じて `ospec brainstorm [path] --topic "..."` で探索を保存する。いずれかが本当に未決のときは、黙って仮定を記録するより durable な decision gate を上げることを優先する。ユーザーが明示的に委任した、または不在のときだけ `design.md` に仮定を記録し、要確認の仮定として明記する | ||
@@ -53,15 +53,13 @@ - change がユーザー選択を待つ必要がある場合は `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:impact --option id:label:impact [--recommended id] [--required]` で durable decision gate を記録し、decision report の `Chat Prompt` または `artifacts/agents/decisions/index.md` を提示してから、`ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` で回答を記録する | ||
| - `ospec execute dispatch [changes/active/<change>] [--task task-id] [--limit N]` で parallel-safe な worker packet batch と `artifacts/agents/execution-session.json` を作成する。各 packet には project session brief snapshot と、capability tier、recommended target、target tool mapping、rationale、required behavior を示す worker profile が含まれる。`ospec execute complete <task-id> ...` で worker 結果を記録する。`--task` は明示的な単一 task、`--limit` は dispatch batch size の上限に使う。これらのコマンドは `artifacts/agents/worker-status.md` も同期し、OSpec artifacts のみを更新し、外部 worker は起動しない。結果が `NEEDS_CONTEXT` または `BLOCKED` の場合、`complete` は `artifacts/agents/blockers/` に blocker escalation を書く | ||
| - dispatch 後は `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` で launch plan を書く。`runtimeAdapter` は検証済み Orca ownership、current harness-native capability、利用可能な target CLI、serial generic current controller の順で解決する。Orca の process 名だけでは ownership の証拠にならない。単一 task dispatch はこの plan を自動生成する | ||
| - multi-worker execution は `runtimeAdapter.selected` に従い、選択 adapter が parallel work をサポートするときだけ安全な batch を並列起動する。通常 task は serial current controller に fallback できるが、independent review には independent adapter が必要 | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 は report-only。L2/L3 では IDE AI が tick -> 各 action の selected runtime adapter で全 `actions[]` を実行 -> heartbeat/result evidence -> 即時 tick を担当する。`actions[]` が空で `pending` がある場合は観察のみで再 dispatch しない | ||
| - `ospec execute orchestrate [changes/active/<change>] --command "..."` は selected adapter または記録された fallback order が explicit CLI orchestration を選択した場合だけ使う | ||
| - selected target-CLI adapter が single-worker runner を必要とする場合、または前の adapter が ownership claim 前に失敗した場合は `--run --command` を使い、`artifacts/agents/worker-runs/` を記録する。その後 `ospec execute collect ...` で result を記録し、必要なら `ospec execute retry` で再 dispatch する | ||
| - dispatch 後は `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` で launch plan を書く。`runtimeAdapter` は current かつ target-bound な model-native subagent capability のみを受け入れる。single-task dispatch はこの plan を自動生成する | ||
| - `runtimeAdapter.selected.nativeSubagent` を実行し、安全な batch だけを並列 dispatch する。capability がない、または期限切れの場合は block し、agent CLI や current controller に fallback しない | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`: L1 は report-only。L2/L3 では IDE AI が tick -> 各 action の `runtimeAdapter.selected.nativeSubagent` で全 `actions[]` を実行 -> heartbeat/result evidence -> 即時 tick を担当する。`actions[]` が空で `pending` がある場合は観察のみで再 dispatch しない | ||
| - agent CLI execution は削除された。`execute orchestrate`、`launch --run --command`、`review --run --command`、`loop watch` は process 起動や run artifact 作成の前に失敗する。native work の再実行には `ospec execute retry` を使う | ||
| - 各 worker task 完了後、`ospec execute review [changes/active/<change>] --task <task-id>` で統合 review と task-scoped `artifacts/agents/review-packages/*.diff` を作成する。reviewer は package を一度読み、広範な Git 探索を繰り返さない。サイズと所要時間は `artifacts/agents/execution-metrics.json` に記録される | ||
| - task graph で `documentation_updates` を有効にした場合、各 task に配列を持たせ(不要なら `[]`)、宣言した docs path を同じ task の `target_files` に含め、archive 前に存在させ、dispatch から complete までの有意な content change evidence を残す。baseline のない旧 run は互換性を保つが未検証と表示する。 | ||
| - すべての task-level review が承認され task graph が完了した後、`--task` なしの `ospec execute review [changes/active/<change>]` で 1 つの統合 final whole-change code reviewer handoff packet を `artifacts/agents/review-dispatches/` に作成する。これは単一 `artifacts/reviews/final-review.md`・1 つの decision | ||
| - explicit `ospec execute review ... --run --command "..."` の場合のみ local reviewer command を実行し、`artifacts/agents/review-runs/` を記録する。`--decision` がある場合は review decision も書き戻せる | ||
| - review artifact が non-`PENDING` decision を持つ場合は `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` で `artifacts/agents/review-feedback-plan.json` と `artifacts/agents/review-feedback-plan.md` を書く。追加作業を dispatch する前に accept、revise、clarify、blocked の handling を明確にし、feedback が scope、direction、API、UI、risk、accepted tradeoffs を変える場合は required user decision gate を作成する | ||
| - debugging が change の一部だった場合、`ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` で `artifacts/agents/debug-evidence.json` を記録する。`CONFIRMED` は root cause の隔離、`FIXED` は verified fix、`BLOCKED` は verify failure を意味する | ||
| - focused test 実行後、`ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` で `artifacts/agents/tdd-evidence.json` を記録する。red は通常、期待どおり失敗する test を記録し、green/refactor は passing result を記録する | ||
| - fresh project verification commands を実行した後、`ospec execute verify [changes/active/<change>] --command "..." --status PASSED` で `artifacts/agents/verification-evidence.json` を記録する。chat summary だけで完了を主張しない | ||
| - fresh project verification commands を実行した後、`ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` で `artifacts/agents/verification-evidence.json` を記録する。chat summary だけで完了を主張しない | ||
| - task graph、execution session、review artifacts、debug evidence、verification checklist を手動編集した後は、`ospec execute sync [changes/active/<change>]` で `artifacts/agents/worker-status.md` を再構築する | ||
@@ -68,0 +66,0 @@ - トークン節約(どの手順も変えない):`ospec execute …` に `--brief` を付けて完全なレポートではなく簡潔な要約を読み、毎ターン `task-graph.json` / `worker-status.md` / `launch-plan.md` 全体を読み直す代わりに `ospec execute status --brief` で各ステップを駆動する——artifact は完全な形でディスクに書かれるので、詳細が必要なときだけ開く |
@@ -37,3 +37,3 @@ --- | ||
| - 执行经典 change 时,不要创建 goal-only 文件,除非用户明确把该工作升级为 goal | ||
| - `Announce-Before-Act`:绝不静默执行流程。宣告 OSpec skill 与阶段、命令与产物、所选 runtime adapter、worker 数量与实际机制,以及阻塞门禁和解锁条件 | ||
| - `Announce-Before-Act`:绝不静默执行流程。宣告 OSpec skill 与阶段、命令与产物、所选 model-native subagent adapter、worker 数量、当前 session capability,以及阻塞门禁和解锁条件 | ||
| - `Brainstorm-First`:每个 goal 开局先做一次简短头脑风暴再锁定设计。把方向、架构、API、数据、UI、风险、范围的未决问题逐个抛给用户,而不是默默假设;需要时用 `ospec brainstorm [path] --topic "..."` 持久化探索。任一项真正开放时,优先升起持久 decision gate 让用户选择,而不是写下静默假设;仅当用户明确让 AI 自行决定或不可用时,才在 `design.md` 写入假设并标注为待确认 | ||
@@ -55,16 +55,14 @@ - 当 change 必须等待用户选择后才能继续时,用 `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:影响 --option id:label:影响 [--recommended id] [--required]` 写入持久 decision gate,向用户展示 decision report 的 `Chat Prompt` 或 `artifacts/agents/decisions/index.md`,再用 `ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` 记录用户选择 | ||
| - 需要 task 级持久交接时,用 `ospec execute dispatch` 生成并行安全的 worker 任务包批次,用 `ospec execute complete` 记录 worker 结果;每个 dispatch packet 都包含 project session brief snapshot 和 worker profile,说明 capability tier、recommended target、target tool mapping、rationale 和 required behavior;required pending user decision 会阻止 dispatch;当结果是 `NEEDS_CONTEXT` 或 `BLOCKED` 时,`complete` 会写入 `artifacts/agents/blockers/`;用 `--task` 指定单个任务,用 `--limit` 限制派发批次大小;每个 worker 返回 `DONE` 或 `DONE_WITH_CONCERNS` 后,用 `ospec execute review [changes/active/<change>] --task <task-id>` 做一次合并 review(一次同时审 spec 符合性与代码质量),依赖任务会等这一次合并 review 通过后才可派发;所有单任务 review 通过且 task graph 完成后,再用不带 `--task` 的 `ospec execute review` 生成一个合并的最终整体 code review 交接包;最终 review 决策不是 `PENDING` 后,用 `ospec execute feedback` 写入 `artifacts/agents/review-feedback-plan.md`;人工改过执行或 review artifacts 后,用 `ospec execute sync` 重建 `worker-status.md` | ||
| - dispatch 后用 `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` 写入启动计划;`runtimeAdapter` 按已验证 Orca ownership、当前 harness-native capability、可用 target CLI、串行 generic current controller 排序。Orca 进程名本身不能证明 ownership | ||
| - 多 worker 执行服从 `runtimeAdapter.selected`:只在所选 adapter 支持并行时启动安全 batch;普通任务可退回串行 current controller,独立 review 必须使用独立 adapter | ||
| - goal 集成循环使用 controller 模式时,不要停在初始化或让用户手工运行 Loop 命令。运行 `ospec loop run [change] --once --json`,通过每个 action 的 `runtimeAdapter.selected` 执行,持久化 heartbeat/result evidence,再无需用户提示地继续 tick;每个 worker 只读取引用的 packet | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`:L1 只报告;L2/L3 由 IDE 主 AI 负责 tick -> 通过每个 action 的所选 runtime adapter 执行全部 `actions[]` -> 写 heartbeat/result evidence -> 立即再 tick。`actions[]` 为空但存在 `pending` 时只观察,绝不能重派 | ||
| - CLI-driven 模式下,`ospec loop watch` 是会话内执行器,会为 action 启动 fresh `claude -p` / `codex exec` 进程,并行执行 ready batch,执行后立即重新观察 evidence,只在没有 ready action 时等待;pause、`STOP`、完成、预算、无进展断路器、理解复核门或进程退出都会停止它。不支持直接 CLI target 时使用 controller 模式 | ||
| - dispatch 后用 `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` 写入启动计划;`runtimeAdapter` 只接受当前、target 匹配的模型原生 subagent capability,并给出 native primitive | ||
| - 执行 `runtimeAdapter.selected.nativeSubagent`,只并行派发安全 batch。capability 缺失或过期时必须阻断并刷新当前模型会话,不得启动 agent CLI 或退回 controller context | ||
| - goal 集成循环使用 controller 模式时,不要停在初始化或让用户手工运行 Loop 命令。运行 `ospec loop run [change] --once --json`,通过每个 action 的 `runtimeAdapter.selected.nativeSubagent` 执行,持久化 heartbeat/result evidence,再无需用户提示地继续 tick;每个 worker 只读取引用的 packet | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`:L1 只报告;L2/L3 由 IDE 主 AI 负责 tick -> 通过每个 action 的 `runtimeAdapter.selected.nativeSubagent` 执行全部 `actions[]` -> 写 heartbeat/result evidence -> 立即再 tick。`actions[]` 为空但存在 `pending` 时只观察,绝不能重派 | ||
| - agent CLI 执行已移除;`loop watch`、`execute orchestrate`、`launch --run --command` 和 `review --run --command` 都会在启动进程或创建 run artifact 前失败 | ||
| - required decision 会阻断所有安全级。L1 不产生可执行 action;L3 还要求 path 和 command allowlist 都非空,并阻断越界目标文件或验证命令 | ||
| - 省 token(不改变任何门禁):`ospec execute …` 命令带 `--brief`,用 `ospec execute status --brief` 驱动每一步,并消费 loop action 引用的 packet path;不要每轮重读或内嵌完整 `task-graph.json`、`worker-status.md`、`launch-plan.md` 或全部 goal 文档——完整产物仍写盘,只在需要细节时打开 | ||
| - 只有所选 adapter 或记录的 fallback order 选择显式 CLI orchestration 时,才用 `ospec execute orchestrate [changes/active/<change>] --command "..."`,并且只运行 adapter 允许的 batch | ||
| - 所选 target-CLI adapter 需要单 worker runner,或前序 adapter 在 claim ownership 前失败时,用 `--run --command`;随后用 `ospec execute collect ...` 记录结果。修复 blocked、needs-context 或 failed work 后,用 `ospec execute retry` 重新派发;已完成任务默认不得 retry,除非显式 `--force` | ||
| - 只有显式使用 `ospec execute review ... --run --command "..."` 时,OSpec 才会运行本地 reviewer 命令并写入 `artifacts/agents/review-runs/`;提供 `--decision` 时可写回单任务或最终 review decision | ||
| - 修复 blocked、needs-context 或 failed native work 后,用 `ospec execute retry` 重新派发;已完成任务默认不得 retry,除非显式 `--force` | ||
| - 调试是 change 的一部分时,用 `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` 记录根因和修复证据;该命令只记录 evidence,不会运行 shell 命令 | ||
| - 运行聚焦测试后,用 `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` 记录 TDD cycle evidence;该命令只记录 evidence,不会运行 shell 命令 | ||
| - 运行最新项目检查后,用 `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` 记录验证证据;该命令只记录 evidence,不会运行 shell 命令 | ||
| - `ospec execute doc-review` 只记录 artifacts,不会启动 reviewer、运行 shell 命令、同步 worker status 或编辑源码;shell 只会通过显式 worktree create/cleanup、fallback `launch --run --command`、`review --run --command`、带 command template 的 fallback `orchestrate`,或已配置的 CLI-driven `ospec loop watch` 执行 | ||
| - 运行最新项目检查后,用 `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` 记录验证证据;该命令只记录 evidence,不会运行 shell 命令 | ||
| - `ospec execute doc-review` 只记录 artifacts,不会启动 reviewer、运行 shell 命令、同步 worker status 或编辑源码;specialist review 必须通过 `runtimeAdapter.selected.nativeSubagent` 执行并绑定真实 child id | ||
| - 对 goal,`artifacts/agents/task-graph.json` 中存在未解决 task 状态、无效依赖、缺失目标文件、缺失验证命令,或顶层 `status` 不是 `completed` 时,不得 archive | ||
@@ -71,0 +69,0 @@ - 实现后每个任务必须完成该任务的一次合并 review(`artifacts/reviews/tasks/<task-id>/review.md`);最终阶段必须完成单一的 `artifacts/reviews/final-review.md`;未解决的单任务或最终 review decision 会阻止 archive |
@@ -35,3 +35,3 @@ --- | ||
| - AI 辅助执行 goal 时,必须在完成 `proposal.md` 后、编辑 `implementation-plan.md`、`tasks.md` 或代码前,先起草或更新 `design.md`。执行经典 change 时,不要创建 goal-only 文件,除非用户明确升级为 goal | ||
| - `Announce-Before-Act`:绝不静默执行流程。宣告 OSpec skill 与阶段、命令与产物、所选 runtime adapter、worker 数量与实际机制,以及阻塞门禁和解锁条件 | ||
| - `Announce-Before-Act`:绝不静默执行流程。宣告 OSpec skill 与阶段、命令与产物、所选 model-native subagent adapter、worker 数量、当前 session capability,以及阻塞门禁和解锁条件 | ||
| - `Brainstorm-First`:每个 goal 开局先做一次简短头脑风暴再锁定设计。把方向、架构、API、数据、UI、风险、范围的未决问题逐个抛给用户,而不是默默假设;需要时用 `ospec brainstorm [path] --topic "..."` 持久化探索。任一项真正开放时,优先升起持久 decision gate 让用户选择,而不是记录静默假设;仅当用户明确让 AI 自行决定或不可用时,才在 `design.md` 写入假设并标注为待确认 | ||
@@ -56,15 +56,13 @@ - 当 change 必须等待用户选择后才能继续时,用 `ospec execute decision [changes/active/<change>] --id <id> --question "..." --option id:label:影响 --option id:label:影响 [--recommended id] [--required]` 写入持久 decision gate,向用户展示 decision report 的 `Chat Prompt` 或 `artifacts/agents/decisions/index.md`,再用 `ospec execute decision [changes/active/<change>] --id <id> --select <option-id>` 记录用户选择 | ||
| - 省 token(不改变任何步骤):`ospec execute …` 命令带 `--brief` 读精简摘要而非完整报告;用 `ospec execute status --brief` 驱动每一步,不要每轮都重读完整的 `task-graph.json`、`worker-status.md` 或 `launch-plan.md`——产物仍完整写盘,只在需要细节时才打开 | ||
| - dispatch 后用 `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` 写入启动计划;`runtimeAdapter` 按已验证 Orca ownership、当前 harness-native capability、可用 target CLI、串行 generic current controller 排序。Orca 进程名本身不能证明 ownership;单任务 dispatch 会自动生成该计划 | ||
| - 多 worker 执行服从 `runtimeAdapter.selected`:只在所选 adapter 支持并行时启动安全 batch;普通任务可退回串行 current controller,独立 review 必须使用独立 adapter | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`:L1 只报告;L2/L3 由 IDE 主 AI 负责 tick -> 通过每个 action 的所选 runtime adapter 执行全部 `actions[]` -> 写 heartbeat/result evidence -> 立即再 tick。`actions[]` 为空但存在 `pending` 时只观察,绝不能重派 | ||
| - 只有所选 adapter 或记录的 fallback order 选择显式 CLI orchestration 时,才用 `ospec execute orchestrate [changes/active/<change>] --command "..."`,并且只运行 adapter 允许的 batch | ||
| - 所选 target-CLI adapter 需要单 worker runner,或前序 adapter 在 claim ownership 前失败时,用 `--run --command` 并写入 `artifacts/agents/worker-runs/`;随后用 `ospec execute collect ...` 记录结果。修复 blocked、needs-context 或 failed work 后,用 `ospec execute retry` 重新派发;已完成任务默认不得 retry,除非显式 `--force` | ||
| - dispatch 后用 `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` 写入启动计划;`runtimeAdapter` 只接受当前、target 匹配的模型原生 subagent capability;单任务 dispatch 会自动生成该计划 | ||
| - 执行 `runtimeAdapter.selected.nativeSubagent`,只并行派发安全 batch。capability 缺失或过期时阻断,不得启动 agent CLI 或退回 controller context | ||
| - `IDE-CONTROLLER-AUTO-DISPATCH`:L1 只报告;L2/L3 由 IDE 主 AI 负责 tick -> 通过每个 action 的 `runtimeAdapter.selected.nativeSubagent` 执行全部 `actions[]` -> 写 heartbeat/result evidence -> 立即再 tick。`actions[]` 为空但存在 `pending` 时只观察,绝不能重派 | ||
| - agent CLI 执行已移除:`execute orchestrate`、`launch --run --command`、`review --run --command` 和 `loop watch` 都会在启动进程或创建 run artifact 前失败。修复 native work 后使用 `ospec execute retry`;已完成任务需显式 `--force` | ||
| - worker 记录 `DONE` 或 `DONE_WITH_CONCERNS` 后,用 `ospec execute review [changes/active/<change>] --task <task-id>` 生成一次合并 review 和范围受控的 `artifacts/agents/review-packages/*.diff`;reviewer 一次读取该 package,不重复做宽范围 Git 探索。packet、report、package 字节数和 task 耗时记录在 `artifacts/agents/execution-metrics.json` | ||
| - task graph 启用 `documentation_updates` 后,每个 task 都必须包含该数组(没有则为 `[]`);声明的 docs 路径必须同时出现在同一 task 的 `target_files` 中、在归档前存在,并具有从 dispatch 到 complete 的有效内容变化证据。没有历史基线的旧流程保持兼容,但必须把证据标记为无法验证。 | ||
| - 所有单任务 review 通过且 task graph 完成后,用不带 `--task` 的 `ospec execute review [changes/active/<change>]` 在 `artifacts/agents/review-dispatches/` 下生成一个合并的最终整体 code review 交接包;它产出单一 `artifacts/reviews/final-review.md`、一道决策 | ||
| - 只有显式使用 `ospec execute review ... --run --command "..."` 时,OSpec 才会运行本地 reviewer 命令并写入 `artifacts/agents/review-runs/`;提供 `--decision` 时可写回单任务或最终 review decision | ||
| - review artifact 有非 `PENDING` 决策后,用 `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` 写入 `artifacts/agents/review-feedback-plan.json` 和 `artifacts/agents/review-feedback-plan.md`;继续派发工作前,必须明确接受、修订、澄清或解除阻塞;当反馈影响范围、方向、API、UI、风险或已接受取舍时创建 required user decision gate | ||
| - 调试是 change 的一部分时,用 `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` 记录 `artifacts/agents/debug-evidence.json`;`CONFIRMED` 表示隔离根因,`FIXED` 表示修复已验证,`BLOCKED` 会让 verify 失败 | ||
| - 运行聚焦测试后,用 `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` 记录 `artifacts/agents/tdd-evidence.json`;red 必须先记录实现前不通过的聚焦测试,green 必须有前置 red `FAILED` 记录,refactor 必须有前置通过的 green/refactor 证据,`SKIPPED` 必须写明具体原因 | ||
| - 运行最新项目验证命令后,用 `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` 记录 `artifacts/agents/verification-evidence.json`;不得只用聊天摘要声称完成 | ||
| - 运行最新项目验证命令后,用 `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` 记录 `artifacts/agents/verification-evidence.json`;不得只用聊天摘要声称完成 | ||
| - 人工修改 task graph、execution session、review artifacts、debug evidence 或 verification checklist 后,用 `ospec execute sync [changes/active/<change>]` 重建 `artifacts/agents/worker-status.md` | ||
@@ -71,0 +69,0 @@ - 对 goal,`tasks.md` 必须从 `artifacts/agents/task-graph.json` 推导;如果任务已存在但上游文档仍是模板,先补上游文档,再对齐任务。对经典 change,`tasks.md` 直接从 `proposal.md` 和实现范围推导 |
@@ -14,11 +14,11 @@ --- | ||
| - **OSpec is the durable state-machine brain.** `ospec loop run --once` first observes the previous task, review, or verification evidence, then emits a bounded batch of action items from `task-graph.json`. Each action carries its capability-based `runtimeAdapter`; execute it through verified Orca, a current harness-native child, a registered target CLI, or the serial current controller, then record durable evidence. | ||
| - **OSpec is the durable state-machine brain.** `ospec loop run --once` first observes the previous task, review, or verification evidence, then emits a bounded batch of action items from `task-graph.json`. Each action carries a target-bound `runtimeAdapter`; execute it only through the current model harness native subagent primitive, then record durable evidence. | ||
| - **IDE controller auto-dispatch is mandatory for executable L2/L3 goals.** L1 is report-only and never starts implementation/review/verifier action batches; for implementation, present the safety-level decision first and wait for the user to choose L2 or L3. After creating or resuming an executable goal, do not stop at "Loop initialized", ask the user to run Loop commands, or wait for another prompt. Once actions are ready, run `ospec loop run <goal-path> --once --json`, immediately consume every returned action through `runtimeAdapter.selected`, record durable evidence as each executor finishes, and tick again. Stop only for an actual required user decision, unavailable independent/isolated executor, blocking safety/plugin gate, configured guard/STOP, terminal failure that needs user authority, explicit user pause, or `done`. | ||
| - **Harness capability is explicit and adapter fallback is evidence-based.** In Codex create executable work with `--target codex --execution-model controller --harness-interactive true --native-subagents supported`; use the equivalent actual target in other IDEs. A target name alone never proves that native children exist. Read `runtimeAdapter` from the launch artifact: verified Orca ownership first, then a current harness-native adapter, a callable target CLI, and finally the serial generic current-controller fallback. Never claim native dispatch when `controllerAvailable` is false. | ||
| - **Executor lifecycle is durable.** After native spawn, Orca terminal creation, or external CLI launch, record `ospec loop heartbeat <goal> --action-item <id> --executor <executor-id>` and refresh the lease while waiting. Record each executor independently with `ospec loop result ...`; do not wait for the whole batch before persisting completed siblings. Use `ospec loop recover --force` only when the prior session/executor is known to be gone. Expired items requeue; completed siblings do not. | ||
| - **Independent document reviewers are bound to the selected adapter executor.** Read `dispatch.runtimeAdapter`, start the specialist through verified Orca, harness-native, or target CLI, immediately claim its real terminal/child/process id, refresh long reviews with `--heartbeat-executor`, and finish with `--complete-executor`. Generic current-controller fallback is forbidden for independent review. Native reviews additionally bind the controller session; all reviews bind executor, timestamp, document hash, and findings provenance. | ||
| - **Harness capability is explicit and target-bound.** In Codex create executable work with `--target codex --execution-model controller --harness-interactive true --native-subagents supported`; use the equivalent actual target in other IDEs. A target name alone never proves that native children exist. `runtimeAdapter.selected` exists only when the capability target matches, the session is current, and native subagents are supported. There is no Orca, target-CLI, or current-controller fallback. | ||
| - **Executor lifecycle is durable.** After native subagent dispatch, record `ospec loop heartbeat <goal> --action-item <id> --executor <child-id>` and refresh the lease while waiting. Record each child independently with `ospec loop result ...`; do not wait for the whole batch before persisting completed siblings. Use `ospec loop recover --force` only when the prior session/child is known to be gone. Expired items requeue; completed siblings do not. | ||
| - **Independent document reviewers are bound to a native child.** Read `dispatch.runtimeAdapter.selected.nativeSubagent`, start one fresh model-native reviewer, immediately claim its real child id, refresh long reviews with `--heartbeat-executor`, and finish with `--complete-executor`. Reviews bind the target, controller session, child, timestamp, document hash, and findings provenance. | ||
| - **Safety level chosen at creation via a decision gate.** Prefer presenting an `AskUserQuestion` for L1/L2/L3 as the first decision; otherwise pass `ospec goal <name> --level L1|L2|L3` (default L1). L1 = report-only (findings go to triage, no code changes); L2 = assisted (real changes but required-decision gates hard-block); L3 = unattended within an allowlist. | ||
| - **Required decisions block every level.** Present each required decision to the user, never auto-select the recommendation, and record the answer with `ospec execute decision ... --select ... --answered-by user` before the loop proceeds. New brainstorm resolutions require the same `--answered-by user` provenance. L3 additionally requires non-empty path and command allowlists; it does not bypass human decision gates. | ||
| - **`/goal` is capability-probed, not inferred from a target name.** `ospec execute launch --primitive goal` produces a native-`/goal` instruction only when the current harness explicitly reports support; otherwise it emits an emulated verify-driven plan. CLI-driven targets use `claude -p` / `codex exec` (never `claude --goal`). | ||
| - **Scheduling is session-bound.** Controller mode re-runs `loop run --once` and consumes the emitted action batch through the selected runtime adapter. Verified Orca and harness-native adapters may open parallel workers; target CLI fallback uses fresh external processes; generic fallback executes ordinary work serially in the current controller. CLI-driven `loop watch` re-observes evidence immediately after a batch and waits only when no action is ready. Both die with the session. | ||
| - **`/goal` is capability-probed, not inferred from a target name.** `ospec execute launch --primitive goal` produces a native-`/goal` instruction only when the current harness explicitly reports support; otherwise the same controller runs the verify-driven loop through native subagents. | ||
| - **Scheduling is session-bound.** Controller mode re-runs `loop run --once` and consumes the emitted action batch through `runtimeAdapter.selected.nativeSubagent`. Codex uses `spawn_agent`/`wait_agent`, Claude uses Task, and other supported harnesses use their own native primitive. If that session capability expires, OSpec blocks instead of starting an agent CLI. | ||
| - **Progress and feedback are artifact-backed.** Task status, task/final review decisions, grouped repair waves, verification evidence, `state.json`, and `run-log.jsonl` carry progress between fresh contexts. Process exit alone does not complete an action. Dispatch only items returned in `actions`; a durable `pending` record with an empty action list is observation state and must not be relaunched. | ||
@@ -66,3 +66,3 @@ - **Guards are enforced before new work.** Pause/STOP, iteration/deadline/token/time budgets, no-progress limits, comprehension-review checkpoints, required decisions, approved document reviews, ready workspace evidence, and L3 allowlists can stop or pause the loop. | ||
| - `Announce-Before-Act`: never run the goal workflow silently. Announce the current skill/stage, the `ospec execute ...` command and artifact, the selected runtime adapter, how many workers are launched, and which task each owns. Name the actual mechanism: Orca terminal, Claude Task, Codex/GPT `spawn_agent`, Gemini `@generalist`, OpenCode `@mention`, target CLI, or serial current controller. When a gate blocks progress, state what is blocked and what unblocks it. | ||
| - `Announce-Before-Act`: never run the goal workflow silently. Announce the current skill/stage, the `ospec execute ...` command and artifact, the selected runtime adapter, how many workers are launched, and which task each owns. Name the actual native mechanism: Claude Task, Codex/GPT `spawn_agent`, Gemini `@generalist`, OpenCode `@mention`, or the registered native target primitive. When a gate blocks progress, state what is blocked and what unblocks it. | ||
| - `Brainstorm-First`: open each goal with a short brainstorming pass before locking design. Surface the open questions for direction, architecture, API, data, UI, risk, and scope, and ask the user one question at a time instead of silently assuming. **NEVER auto-select the recommended option or resolve a decision gate yourself — `recommended` is only a hint to show the user. Present every gate to the user and wait for their actual choice; required gates block implementation and dispatch until the user answers. Do not run the whole goal in one shot without asking.** Persist exploration with `ospec brainstorm [path] --topic "..."` when useful, and do not leave a brainstorm as an unanswered template — ask the user its decision gates and record each answer with `ospec brainstorm resolve [path] --brainstorm <id> --gate <gate-id> --select <option-id> --answered-by user` so it has a result. Resolve the brainstorm while its change is the active change (or pass `--change <name>`) so it links to that change and archives together with it — the brainstorm directory name need not match the change name. When any of those is genuinely open, prefer raising a durable decision gate over guessing: `ospec execute decision [changes/active/<goal>] --id <id> --question "..." --option id:label:impact --required`, present the decision report `Chat Prompt` or `artifacts/agents/decisions/index.md`, then record the answer with `--select <option-id> --answered-by user`. Only record an autonomous assumption in `design.md` when the user explicitly defers or is unavailable, and label it as an assumption to confirm. Present options using the best interactive mechanism your harness has: a native question UI (Claude Code `AskUserQuestion`, Gemini `ask_user`) if available, otherwise your plan/approval UI (Codex Plan mode) if available, otherwise the decision report `Chat Prompt` as plain chat text. `ospec session hook --target claude --apply` installs hooks that re-affirm this contract and hard-block subagent dispatch while a required decision is pending. On harnesses without a native picker or plan UI present the decision report `Chat Prompt` in chat instead — the asking step and the `ospec execute dispatch` block on required pending decisions are identical on every harness, so you always ask the user, only the presentation differs. | ||
@@ -84,3 +84,3 @@ - `Explicit-Verification-Intent`: a user-requested verification surface such as `$browser`, a real browser E2E run, or another named skill/tool is a hard requirement. Immediately persist it with `ospec execute require-verification <goal> --id <id> --kind browser|e2e|manual --description "..."`; record successful evidence with `ospec execute verify ... --satisfies <id>`. Final verification and archive remain blocked while required evidence is missing or stale. Do not auto-select or recommend a verification option that removes it. | ||
| 10. Use `ospec execute decision` for direction, architecture, API, UI, risk, or scope choices that need explicit user selection, and always include `--answered-by user` when persisting the user's answer. Persist every named browser/E2E/manual verification requirement before implementation. | ||
| 11. Use `ospec execute workspace`, `dispatch`, `launch`, `complete`, `review`, `feedback`, `repair`, `sync`, `tdd`, `debug`, and `verify` as needed for the full workflow. Run `ospec loop run <goal-path> --once --json`, resolve each emitted packet through launch artifact `runtimeAdapter`, execute the safe batch with the selected adapter, record completion/review evidence as each worker finishes, and tick again without waiting for another user prompt. Orca requires callable CLI status plus current-worktree ownership proof; process names are insufficient. Failed preferred adapters continue through the recorded fallback order unless strict mode or reviewer independence makes fallback unsafe. Model profiles are logical and resolve through `.skillrc.workflow.model_profiles`; command runners ingest `OSPEC_USAGE_FILE` automatically and `complete --usage-file` remains a manual fallback. Require reviewers to write Markdown plus sibling structured `*.findings.json`. If final review is `NEEDS_CHANGES`, create one grouped repair task instead of one worker per finding. | ||
| 11. Use `ospec execute workspace`, `dispatch`, `launch`, `complete`, `review`, `feedback`, `repair`, `sync`, `tdd`, `debug`, and `verify` as needed for the full workflow. Run `ospec loop run <goal-path> --once --json`, dispatch every emitted packet through `runtimeAdapter.selected.nativeSubagent`, record completion/review evidence as each child finishes, and tick again without waiting for another user prompt. Never start Orca, Codex, Claude, or another agent CLI as a fallback. Model profiles are logical and resolve through `.skillrc.workflow.model_profiles`; `complete --usage-file` may record provider usage. Require reviewers to write Markdown plus sibling structured `*.findings.json`. If final review is `NEEDS_CHANGES`, create one grouped repair task instead of one worker per finding. | ||
| 12. Do not archive while task graph status, task-level reviews, final reviews, worker status, required user decisions, document reviews, or verification evidence are incomplete. | ||
@@ -96,10 +96,9 @@ 13. Use `ospec execute finish` before finalize when the goal used task graph execution or worktree planning. | ||
| ospec status [path] | ||
| ospec goal <goal-name> [path] [--flags flag1,flag2] [--level L1|L2|L3] [--target ...] [--execution-model controller|cli-driven] [--harness-interactive true|false] [--native-subagents supported|unknown|unsupported] | ||
| ospec goal <goal-name> [path] [--flags flag1,flag2] [--level L1|L2|L3] [--target ...] [--execution-model controller] [--harness-interactive true|false] [--native-subagents supported|unknown|unsupported] | ||
| ospec execute status [changes/active/<goal>] --brief | ||
| ospec loop status [changes/active/<goal>] | ||
| ospec loop run [changes/active/<goal>] [--once] [--json] # prefer JSON for runtime-adapter dispatch | ||
| ospec loop watch [changes/active/<goal>] [--target claude|codex|gpt] [--interval 10m] [--max-ticks N] [--timeout-ms N] [--dry-run] | ||
| ospec loop tick-plan [changes/active/<goal>] | ||
| ospec loop level [changes/active/<goal>] <L1|L2|L3> | ||
| ospec loop configure [changes/active/<goal>] --execution-model controller|cli-driven --max-parallel N --fresh-context true | ||
| ospec loop configure [changes/active/<goal>] --execution-model controller --max-parallel N --fresh-context true | ||
| ospec loop configure [changes/active/<goal>] --max-iterations N --budget-tokens N --budget-minutes N --expires-at <ISO-8601> | ||
@@ -136,3 +135,3 @@ ospec loop configure [changes/active/<goal>] --allow-path <path> --allow-command <prefix> --test-command <command> | ||
| ospec execute require-verification [changes/active/<goal>] --id <id> --kind browser|e2e|manual --description "..." | ||
| ospec execute verify [changes/active/<goal>] --command "..." --status PASSED --satisfies <id> | ||
| ospec execute verify [changes/active/<goal>] --command "..." --status PASSED --exit-code 0 --satisfies <id> | ||
| ospec execute sync [changes/active/<goal>] | ||
@@ -148,7 +147,7 @@ ospec execute finish [changes/active/<goal>] [--target main] [--remote origin] | ||
| - Do not use the goal workflow for routine one-file or low-risk changes unless the user asks for it. | ||
| - Resolve execution from launch artifact `runtimeAdapter`, not process-name guesses. Orca is selected only after its CLI proves current-worktree ownership; current harness-native capabilities outrank target CLI and generic fallbacks. | ||
| - When the selected adapter supports parallel execution, launch the complete parallel-safe batch and never duplicate completed siblings. When only `generic-current-controller` remains, execute ordinary work serially and report that no child process was created. | ||
| - Adapter probe failure is not itself a stop condition. Continue through the recorded fallback order. Block only when strict preference is explicit or the task requires an independent/isolated executor that no available adapter can provide. | ||
| - Resolve execution from `runtimeAdapter.selected.nativeSubagent`, never from a process name or PATH probe. The capability must be current and bound to the configured target. | ||
| - Launch the complete parallel-safe batch through the model harness native primitive and never duplicate completed siblings. | ||
| - Missing, mismatched, future-dated, or expired native capability is a hard dispatch gate. Refresh the current model session capability; do not fall back to an agent CLI or the controller context. | ||
| - Treat the emitted packet path as authoritative context. Do not paste the whole goal into each worker or reuse a reviewer context for implementation. | ||
| - Do not claim goal closeout while document reviews, task graph, final reviews, worker status, required user decisions, or verification evidence are incomplete. | ||
| - If real project tests exist, run them separately before recording verification evidence. |
@@ -63,15 +63,14 @@ --- | ||
| - عندما تحتاج إلى handoff artifact دائم على مستوى task، استخدم `ospec execute dispatch` لإنشاء batch آمن للتوازي من worker packets و`artifacts/agents/execution-session.json`؛ يتضمن كل packet project session brief snapshot وworker profile يوضح capability tier وrecommended target وtarget tool mapping وrationale وrequired behavior؛ واستخدم `--task` لمهمة واحدة صريحة و`--limit` لتحديد حجم dispatch batch، واستخدم `ospec execute complete` لتسجيل نتائج worker؛ وعندما يسجل `complete` النتيجة `NEEDS_CONTEXT` أو `BLOCKED` يتم إنشاء `artifacts/agents/blockers/` | ||
| - بعد dispatch، استخدم `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` لكتابة `launch-plan.json` و`launch-plan.md`؛ يحل `runtimeAdapter` بالترتيب: Orca ownership موثّق، ثم current harness-native capability، ثم target CLI متاح، ثم serial generic current controller. لا يكفي اسم process لإثبات Orca ownership | ||
| - يتبع multi-worker execution القيمة `runtimeAdapter.selected`، ولا يعمل بالتوازي إلا عندما يدعم adapter المختار ذلك. يمكن للـ task العادي الرجوع إلى serial current controller، أما independent review فيتطلب independent adapter | ||
| - استخدم `ospec execute orchestrate [changes/active/<change>] --command "..." [--limit N] [--max-rounds N] [--timeout-ms N]` فقط عندما يختار selected adapter أو fallback order المسجل مسار explicit CLI orchestration | ||
| - استخدم `--run --command` عندما يحتاج selected target-CLI adapter إلى single-worker runner أو يفشل adapter سابق قبل claim ownership. بعدها استخدم `ospec execute collect [changes/active/<change>] [--task task-id] [--run run-id]` لتسجيل النتيجة. بعد إصلاح blocked أو needs-context أو failed work استخدم `ospec execute retry` لإعادة dispatch؛ المهام المكتملة تحتاج `--force` | ||
| - بعد dispatch، استخدم `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` لكتابة `launch-plan.json` و`launch-plan.md`؛ لا يختار `runtimeAdapter.selected.nativeSubagent` إلا current session capability مطابقة للـ target وغير منتهية | ||
| - يتبع multi-worker execution القيمة `runtimeAdapter.selected.nativeSubagent`، ولا يعمل بالتوازي إلا عندما يدعم native adapter المختار ذلك. يجب block عند غياب capability أو انتهائها أو عدم مطابقة target، من دون fallback إلى Orca أو agent CLI أو current controller | ||
| - أزيلت `execute orchestrate` و`launch --run --command` و`review --run --command` و`loop watch`، وتفشل قبل إنشاء أي process أو run artifact. بعد إصلاح blocked أو needs-context أو failed work استخدم `ospec execute retry` لإعادة dispatch؛ المهام المكتملة تحتاج `--force` | ||
| - يزامن `ospec execute dispatch` و`complete` أيضا `artifacts/agents/worker-status.md`؛ وبعد تعديل task graph أو execution session أو review artifacts أو debug evidence أو verification checklist يدويا استخدم `ospec execute sync` لإعادة بناء حالة worker | ||
| - بعد اكتمال كل worker task، استخدم `ospec execute review [changes/active/<change>] --task <task-id>` لإنشاء حزمة code reviewer موحدة واحدة تغطي spec compliance وcode quality في تمريرة واحدة. يحفظ قرار task-level review داخل `artifacts/reviews/tasks/<task-id>/review.md` وتبقى المهام التابعة محجوبة حتى اعتماد تلك المراجعة الموحدة الواحدة | ||
| - بعد اعتماد كل task-level reviews واكتمال task graph، استخدم `ospec execute review [changes/active/<change>]` من دون `--task` لإنشاء حزمة code review نهائية موحدة واحدة تتضمن project session brief snapshot داخل `artifacts/agents/review-dispatches/`؛ وهي مراجعة واحدة تكتب قرار `artifacts/reviews/final-review.md` واحد | ||
| - لا يشغّل OSpec local reviewer command إلا عند استخدام `ospec execute review ... --run --command "..."` صراحة؛ يسجل ذلك `artifacts/agents/review-runs/` ويمكنه تحديث review artifact عند تمرير `--decision` | ||
| - أرسل review packet إلى fresh model-native reviewer subagent؛ لا يشغّل OSpec local reviewer CLI. بعد اكتمال reviewer سجّل matching decision وevidence | ||
| - بعد أن يحتوي review artifact على قرار غير `PENDING`، استخدم `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` لكتابة `artifacts/agents/review-feedback-plan.json` و`artifacts/agents/review-feedback-plan.md`؛ حدد accept أو revise أو clarify أو blocked قبل dispatch عمل إضافي، وأنشئ required user decision عندما يغير feedback scope أو direction أو API أو UI أو risk أو accepted tradeoffs | ||
| - عندما يكون debugging جزءا من change، استخدم `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` لتسجيل root-cause وfix evidence داخل `artifacts/agents/debug-evidence.json` | ||
| - بعد تشغيل focused tests، استخدم `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` لتسجيل TDD cycle evidence داخل `artifacts/agents/tdd-evidence.json` | ||
| - بعد تشغيل project checks حديثة، استخدم `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` لتسجيل verification evidence داخل `artifacts/agents/verification-evidence.json` | ||
| - `ospec session` و`ospec execute bootstrap` و`handoff` و`doc-review` و`workspace` وplan-mode `worktree` و`finish` و`dispatch` و`launch` و`collect` و`retry` و`complete` و`review` و`debug` و`tdd` و`verify` و`sync` تحدّث OSpec artifacts فقط؛ وباستثناء قراءة `workspace` و`worktree` و`finish` لحالة git، لا تحرر ملفات source في المشروع مباشرة. يشغّل controller الـ workers عبر runtime adapter المختار | ||
| - بعد تشغيل project checks حديثة، استخدم `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` لتسجيل verification evidence داخل `artifacts/agents/verification-evidence.json` | ||
| - `ospec session` و`ospec execute bootstrap` و`handoff` و`doc-review` و`workspace` وplan-mode `worktree` و`finish` و`dispatch` و`launch` و`collect` و`retry` و`complete` و`review` و`debug` و`tdd` و`verify` و`sync` تحدّث OSpec artifacts فقط؛ وباستثناء قراءة `workspace` و`worktree` و`finish` لحالة git، لا تحرر ملفات source في المشروع مباشرة. يوزّع controller الـ workers فقط عبر model-native subagent adapter المختار | ||
| - في goal لا تؤرشف عندما يحتوي task graph على حالات غير محسومة أو اعتماديات غير صالحة أو تفاصيل تنفيذ ناقصة أو عندما لا يكون `status` العلوي `completed` | ||
@@ -78,0 +77,0 @@ - في goal يسجل `artifacts/agents/worker-status.md` حالات implementer وspec reviewer وquality reviewer وcontroller |
@@ -63,15 +63,14 @@ --- | ||
| - Use `ospec execute dispatch` to create a parallel-safe batch of worker packets and `artifacts/agents/execution-session.json` when task-level handoff needs a durable artifact; each packet includes the project session brief snapshot and a worker profile with capability tier, recommended target, target tool mapping, rationale, and required behavior; use `--task` for one explicit task and `--limit` to cap dispatch batch size; use `ospec execute complete` to record worker results, and expect `artifacts/agents/blockers/` when completion records `NEEDS_CONTEXT` or `BLOCKED` | ||
| - Use `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` after dispatch to write `launch-plan.json` and `launch-plan.md`; `runtimeAdapter` resolves verified Orca ownership, current harness-native capability, an available target CLI, then the serial generic current controller. A process name alone never proves Orca ownership | ||
| - Multi-worker execution follows `runtimeAdapter.selected`; parallelize only when the selected adapter supports it. Ordinary work may fall back to the serial current controller, while independent review requires an independent adapter | ||
| - Use `ospec execute orchestrate [changes/active/<change>] --command "..." [--limit N] [--max-rounds N] [--timeout-ms N]` only when the selected adapter or recorded fallback order chooses explicit CLI orchestration; run only the adapter-supported batch | ||
| - Use `--run --command` with `ospec execute launch ... --run --command "..."` when the selected target-CLI adapter needs a single-worker runner or an earlier adapter fails before claiming ownership. Then use `ospec execute collect [changes/active/<change>] [--task task-id] [--run run-id]` to record the result. Use `ospec execute retry` to reopen corrected blocked, needs-context, or failed work; completed tasks require `--force` | ||
| - Use `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` after dispatch to write `launch-plan.json` and `launch-plan.md`; only a current, target-bound session capability may select `runtimeAdapter.selected.nativeSubagent` | ||
| - Multi-worker execution follows `runtimeAdapter.selected.nativeSubagent`; parallelize only when the selected native adapter supports it. Missing, expired, or target-mismatched capability blocks execution with no Orca, agent CLI, or current-controller fallback | ||
| - `execute orchestrate`, `launch --run --command`, `review --run --command`, and `loop watch` are removed and fail before creating any process or run artifact. Use `ospec execute retry` to reopen corrected blocked, needs-context, or failed work; completed tasks require `--force` | ||
| - `ospec execute dispatch` and `complete` also sync `artifacts/agents/worker-status.md`; use `ospec execute sync` after manual task graph, execution-session, review artifact, debug evidence, or verification checklist edits | ||
| - After each worker task completes, use `ospec execute review [changes/active/<change>] --task <task-id>` to create one combined code reviewer packet covering spec compliance and code quality in a single pass. The task-level decision lives at `artifacts/reviews/tasks/<task-id>/review.md`, and dependent tasks stay blocked until that one combined review is approved | ||
| - After all task-level reviews are approved and the task graph is complete, use `ospec execute review [changes/active/<change>]` without `--task` to create one combined final whole-change code reviewer packet with the project session brief snapshot under `artifacts/agents/review-dispatches/`; it is a single review that writes one `artifacts/reviews/final-review.md` decision | ||
| - Use `ospec execute review ... --run --command "..."` only for explicit local reviewer execution; it writes `artifacts/agents/review-runs/` and can update the matching review artifact when `--decision` is provided | ||
| - Dispatch each review packet to a fresh model-native reviewer subagent; OSpec never runs a local reviewer CLI. Record the matching review decision and evidence after completion | ||
| - Use `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` after a review artifact has a non-`PENDING` decision to write `artifacts/agents/review-feedback-plan.json` and `artifacts/agents/review-feedback-plan.md`; handle feedback as accept, revise, clarify, or blocked before dispatching more work, and create a required user decision when feedback changes scope, direction, API, UI, risk, or accepted tradeoffs | ||
| - Use `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` when debugging is part of the change to record root-cause and fix evidence under `artifacts/agents/debug-evidence.json` | ||
| - Use `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` after focused test runs to record TDD cycle evidence under `artifacts/agents/tdd-evidence.json` | ||
| - Use `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` after fresh project checks to record verification evidence under `artifacts/agents/verification-evidence.json` | ||
| - `ospec session` and `ospec execute bootstrap`, `handoff`, `doc-review`, `workspace`, plan-mode `worktree`, `finish`, `dispatch`, `launch`, `collect`, `retry`, `complete`, `review`, `debug`, `tdd`, `verify`, and `sync` update OSpec artifacts only; except for `workspace`, `worktree`, and `finish` reading git state, they do not edit project source files directly. The controller launches workers through the selected runtime adapter; shell commands run only through explicit worktree, runner, orchestration, or selected external-adapter execution | ||
| - Use `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` after fresh project checks to record verification evidence under `artifacts/agents/verification-evidence.json` | ||
| - `ospec session` and `ospec execute bootstrap`, `handoff`, `doc-review`, `workspace`, plan-mode `worktree`, `finish`, `dispatch`, `launch`, `collect`, `retry`, `complete`, `review`, `debug`, `tdd`, `verify`, and `sync` update OSpec artifacts only; except for `workspace`, `worktree`, and `finish` reading git state, they do not edit project source files directly. The controller dispatches workers only through the selected model-native subagent adapter | ||
| - Do not archive while task graph statuses are unresolved, dependencies are invalid, execution details are missing, or top-level `status` is not `completed` | ||
@@ -78,0 +77,0 @@ - `artifacts/agents/worker-status.md` records implementer, spec reviewer, quality reviewer, and controller statuses |
@@ -63,15 +63,14 @@ --- | ||
| - task-level の永続 handoff artifact が必要な場合は `ospec execute dispatch` で parallel-safe な worker packet batch と `artifacts/agents/execution-session.json` を作成する。各 packet には project session brief snapshot と、capability tier、recommended target、target tool mapping、rationale、required behavior を示す worker profile が含まれる。`--task` は明示的な単一 task、`--limit` は dispatch batch size の上限に使い、`ospec execute complete` で worker 結果を記録する。`complete` が `NEEDS_CONTEXT` または `BLOCKED` を記録した場合は `artifacts/agents/blockers/` が生成される | ||
| - dispatch 後は `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` で `launch-plan.json` と `launch-plan.md` を書く。`runtimeAdapter` は検証済み Orca ownership、current harness-native capability、利用可能な target CLI、serial generic current controller の順で解決する。Orca の process 名だけでは ownership の証拠にならない | ||
| - multi-worker execution は `runtimeAdapter.selected` に従い、選択 adapter が parallel work をサポートするときだけ安全な batch を並列起動する。通常 task は serial current controller に fallback できるが、independent review には independent adapter が必要 | ||
| - `ospec execute orchestrate [changes/active/<change>] --command "..." [--limit N] [--max-rounds N] [--timeout-ms N]` は selected adapter または記録された fallback order が explicit CLI orchestration を選択した場合だけ使う | ||
| - selected target-CLI adapter が single-worker runner を必要とする場合、または前の adapter が ownership claim 前に失敗した場合は `--run --command` を使う。その後 `ospec execute collect [changes/active/<change>] [--task task-id] [--run run-id]` で result を記録する。修正済み blocked/needs-context/failed work は `ospec execute retry` で再 dispatch する。完了済み task は `--force` が必要 | ||
| - dispatch 後は `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` で `launch-plan.json` と `launch-plan.md` を書く。target と一致し、有効期限内の current session capability だけが `runtimeAdapter.selected.nativeSubagent` を選択できる | ||
| - multi-worker execution は `runtimeAdapter.selected.nativeSubagent` に従い、選択された native adapter が parallel work をサポートするときだけ安全な batch を並列起動する。capability がない、期限切れ、または target 不一致の場合は block し、Orca、agent CLI、current controller に fallback しない | ||
| - `execute orchestrate`、`launch --run --command`、`review --run --command`、`loop watch` は削除され、process や run artifact を作る前に失敗する。修正済み blocked/needs-context/failed work は `ospec execute retry` で再 dispatch する。完了済み task は `--force` が必要 | ||
| - `ospec execute dispatch` と `complete` は `artifacts/agents/worker-status.md` も同期する。task graph、execution session、review artifacts、debug evidence、verification checklist を手動編集した後は `ospec execute sync` で worker 状態を再構築する | ||
| - 各 worker task 完了後、`ospec execute review [changes/active/<change>] --task <task-id>` を使い、spec compliance と code quality を一度に確認する統合 code reviewer handoff packet を 1 つ作成する。task-level review decision は `artifacts/reviews/tasks/<task-id>/review.md` に保存され、その 1 回の統合 review が承認されるまで dependent task は dispatch されない | ||
| - すべての task-level review が承認され task graph が完了した後、`--task` なしの `ospec execute review [changes/active/<change>]` で project session brief snapshot を含む 1 つの統合 final whole-change code reviewer handoff packet を `artifacts/agents/review-dispatches/` に作成する。これは単一 `artifacts/reviews/final-review.md`・1 つの decision | ||
| - explicit `ospec execute review ... --run --command "..."` の場合のみ local reviewer command を実行し、`artifacts/agents/review-runs/` を書く。`--decision` がある場合は matching review artifact を更新できる | ||
| - review packet は fresh model-native reviewer subagent に dispatch する。OSpec は local reviewer CLI を実行しない。reviewer 完了後に matching decision と evidence を記録する | ||
| - review artifact が non-`PENDING` decision を持つ場合は `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` で `artifacts/agents/review-feedback-plan.json` と `artifacts/agents/review-feedback-plan.md` を書く。追加作業を dispatch する前に accept、revise、clarify、blocked の handling を明確にし、feedback が scope、direction、API、UI、risk、accepted tradeoffs を変える場合は required user decision を作成する | ||
| - debugging が change の一部だった場合、`ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` で `artifacts/agents/debug-evidence.json` に root cause と fix evidence を記録する | ||
| - focused test 実行後、`ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` で `artifacts/agents/tdd-evidence.json` に TDD cycle evidence を記録する | ||
| - fresh project checks を実行した後、`ospec execute verify [changes/active/<change>] --command "..." --status PASSED` で `artifacts/agents/verification-evidence.json` に verification evidence を記録する | ||
| - `ospec session` と `ospec execute bootstrap`、`handoff`、`doc-review`、`workspace`、plan-mode `worktree`、`finish`、`dispatch`、`launch`、`collect`、`retry`、`complete`、`review`、`debug`、`tdd`、`verify`、`sync` は OSpec artifacts のみを更新する。`workspace`、`worktree`、`finish` が git state を読む場合を除き、project source file は直接編集しない。controller は選択された runtime adapter で worker を起動する | ||
| - fresh project checks を実行した後、`ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` で `artifacts/agents/verification-evidence.json` に verification evidence を記録する | ||
| - `ospec session` と `ospec execute bootstrap`、`handoff`、`doc-review`、`workspace`、plan-mode `worktree`、`finish`、`dispatch`、`launch`、`collect`、`retry`、`complete`、`review`、`debug`、`tdd`、`verify`、`sync` は OSpec artifacts のみを更新する。`workspace`、`worktree`、`finish` が git state を読む場合を除き、project source file は直接編集しない。controller は選択された model-native subagent adapter だけで worker を dispatch する | ||
| - goal では task graph に未解決状態、無効な依存関係、不足した実行詳細、またはトップレベル `status` が `completed` でない状態がある場合は archive しない | ||
@@ -78,0 +77,0 @@ - goal では `artifacts/agents/worker-status.md` が implementer、spec reviewer、quality reviewer、controller の状態を記録する |
@@ -63,15 +63,14 @@ --- | ||
| - 需要 task 级持久交接时,用 `ospec execute dispatch` 生成并行安全的 worker 任务包批次和 `artifacts/agents/execution-session.json`;每个 packet 都包含 project session brief snapshot 和 worker profile,说明 capability tier、recommended target、target tool mapping、rationale 和 required behavior,避免不同工具交接时误判读上下文、编辑、验证和记录完成的边界;用 `--task` 指定单个任务,用 `--limit` 限制派发批次大小;用 `ospec execute complete` 记录 worker 结果;当 complete 记录 `NEEDS_CONTEXT` 或 `BLOCKED` 时,会生成 `artifacts/agents/blockers/` | ||
| - dispatch 后用 `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json]` 写入 `launch-plan.json` 和 `launch-plan.md`;`runtimeAdapter` 按已验证 Orca ownership、当前 harness-native capability、可用 target CLI、串行 generic current controller 排序。Orca 进程名本身不能证明 ownership | ||
| - 多 worker 执行服从 `runtimeAdapter.selected`:只在所选 adapter 支持并行时启动安全 batch;普通任务可退回串行 current controller,独立 review 必须使用独立 adapter | ||
| - 只有所选 adapter 或记录的 fallback order 选择显式 CLI orchestration 时,才用 `ospec execute orchestrate [changes/active/<change>] --command "..." [--limit N] [--max-rounds N] [--timeout-ms N]`,并且只运行 adapter 允许的 batch | ||
| - 所选 target-CLI adapter 需要单 worker runner,或前序 adapter 在 claim ownership 前失败时,用 `--run --command`(即 `ospec execute launch ... --run --command "..."`);随后用 `ospec execute collect [changes/active/<change>] [--task task-id] [--run run-id]` 记录结果。修复 blocked、needs-context 或 failed work 后,用 `ospec execute retry` 重新派发;已完成任务必须显式 `--force` 才能 retry | ||
| - dispatch 后用 `ospec execute launch [changes/active/<change>] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--dry-run] [--json]` 写入 `launch-plan.json` 和 `launch-plan.md`;只有当前有效且 target 匹配的 session capability 才能选择 `runtimeAdapter.selected.nativeSubagent` | ||
| - 多 worker 执行服从 `runtimeAdapter.selected.nativeSubagent`:只在所选 native adapter 支持并行时启动安全 batch。capability 缺失、过期或 target 不匹配时必须阻断,不能回退到 Orca、agent CLI 或 current controller | ||
| - `execute orchestrate`、`launch --run --command`、`review --run --command` 与 `loop watch` 已移除,并在任何进程或 run artifact 创建前失败。修复 blocked、needs-context 或 failed work 后,用 `ospec execute retry` 重新派发;已完成任务必须显式 `--force` 才能 retry | ||
| - `ospec execute dispatch` 与 `complete` 也会同步 `artifacts/agents/worker-status.md`;人工修改 task graph、execution session、review artifacts、debug evidence 或 verification checklist 后,用 `ospec execute sync` 重建 worker 状态 | ||
| - 每个 worker task 完成后,用 `ospec execute review [changes/active/<change>] --task <task-id>` 生成一个合并的 code reviewer 交接包(一次同时审 spec 符合性与代码质量)。task 级 review 决策写入 `artifacts/reviews/tasks/<task-id>/review.md`,依赖任务会等这一次合并 review 通过后才可派发 | ||
| - 所有 task 级 review 通过且 task graph 完成后,用不带 `--task` 的 `ospec execute review [changes/active/<change>]` 在 `artifacts/agents/review-dispatches/` 下生成一个合并的最终整体 code review 交接包;它产出单一 `artifacts/reviews/final-review.md`、一道决策 | ||
| - 只有显式使用 `ospec execute review ... --run --command "..."` 时才运行本地 reviewer 命令,并写入 `artifacts/agents/review-runs/`;提供 `--decision` 时可写回对应 review artifact | ||
| - review packet 必须交给新的 model-native reviewer subagent;OSpec 不运行本地 reviewer CLI。reviewer 完成后写回对应 decision 与 evidence | ||
| - review artifact 有非 `PENDING` 决策后,用 `ospec execute feedback [changes/active/<change>] [--stage spec|quality]` 写入 `artifacts/agents/review-feedback-plan.json` 和 `artifacts/agents/review-feedback-plan.md`;继续派发工作前要明确接受、修订、澄清或阻塞处理;当反馈改变范围、方向、API、UI、风险或已接受取舍时创建 required user decision | ||
| - 调试是 change 的一部分时,用 `ospec execute debug [changes/active/<change>] --phase reproduce|isolate|hypothesize|fix|verify --symptom "..." --root-cause "..." --status FIXED` 在 `artifacts/agents/debug-evidence.json` 下记录根因和修复证据 | ||
| - 运行聚焦测试后,用 `ospec execute tdd [changes/active/<change>] --phase red|green|refactor --command "..." --status ...` 在 `artifacts/agents/tdd-evidence.json` 下记录 TDD cycle evidence | ||
| - 运行最新项目检查后,用 `ospec execute verify [changes/active/<change>] --command "..." --status PASSED` 在 `artifacts/agents/verification-evidence.json` 下记录验证证据 | ||
| - `ospec session` 以及 `ospec execute bootstrap`、`handoff`、`doc-review`、`workspace`、plan 模式 `worktree`、`finish`、`dispatch`、`launch`、`collect`、`retry`、`complete`、`review`、`debug`、`tdd`、`verify` 与 `sync` 只更新 OSpec artifacts;除 `workspace`、`worktree` 与 `finish` 会读取 git 状态外,不直接编辑项目源码。controller 按所选 runtime adapter 启动 worker;shell 只通过显式 worktree、runner、orchestration 或已选择的外部 adapter 执行 | ||
| - 运行最新项目检查后,用 `ospec execute verify [changes/active/<change>] --command "..." --status PASSED --exit-code 0` 在 `artifacts/agents/verification-evidence.json` 下记录验证证据 | ||
| - `ospec session` 以及 `ospec execute bootstrap`、`handoff`、`doc-review`、`workspace`、plan 模式 `worktree`、`finish`、`dispatch`、`launch`、`collect`、`retry`、`complete`、`review`、`debug`、`tdd`、`verify` 与 `sync` 只更新 OSpec artifacts;除 `workspace`、`worktree` 与 `finish` 会读取 git 状态外,不直接编辑项目源码。controller 只通过所选 model-native subagent adapter 派发 worker | ||
| - task graph 存在未解决状态、无效依赖、缺失执行细节,或顶层 `status` 不是 `completed` 时不得归档 | ||
@@ -78,0 +77,0 @@ - `artifacts/agents/worker-status.md` 记录 implementer、spec reviewer、quality reviewer 和 controller 状态 |
+5
-6
@@ -65,3 +65,3 @@ #!/usr/bin/env node | ||
| const services_1 = require("./services"); | ||
| const CLI_VERSION = '1.8.1'; | ||
| const CLI_VERSION = '1.8.2'; | ||
| function showInitUsage() { | ||
@@ -158,3 +158,3 @@ console.log('Usage: ospec init [root-dir] [--summary "..."] [--tech-stack node,react] [--architecture "..."] [--document-language en-US|zh-CN|ja-JP|ar]'); | ||
| return commandName === 'goal' | ||
| ? 'Usage: ospec goal <goal-name> [root-dir] [--flags flag1,flag2] [--level L1|L2|L3] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--execution-model controller|cli-driven] [--harness-interactive true|false] [--native-subagents supported|unknown|unsupported] [--native-goal supported|unknown|unsupported]' | ||
| ? 'Usage: ospec goal <goal-name> [root-dir] [--flags flag1,flag2] [--level L1|L2|L3] [--target codex|gpt|claude|gemini|opencode|cursor|copilot] [--execution-model controller] [--harness-interactive true|false] [--native-subagents supported|unknown|unsupported] [--native-goal supported|unknown|unsupported]' | ||
| : 'Usage: ospec new <change-name> [root-dir] [--flags flag1,flag2]'; | ||
@@ -244,4 +244,4 @@ } | ||
| if (executionModelValue !== undefined) { | ||
| if (executionModelValue !== 'controller' && executionModelValue !== 'cli-driven') { | ||
| console.error(`Invalid --execution-model value for goal: ${executionModelValue} (expected controller or cli-driven)`); | ||
| if (executionModelValue !== 'controller') { | ||
| console.error(`Invalid --execution-model value for goal: ${executionModelValue} (only controller is supported; agent CLI execution was removed)`); | ||
| process.exit(1); | ||
@@ -517,3 +517,3 @@ } | ||
| run [action] [path] Explicit queue runner helpers (start, status, step, resume, stop) | ||
| execute [action] [path] Task graph controller helpers (bootstrap, handoff, doc-review, status, next, workspace, worktree, finish, dispatch, orchestrate, complete, repair) | ||
| execute [action] [path] Task graph controller helpers (bootstrap, handoff, doc-review, status, next, workspace, worktree, finish, dispatch, launch, complete, repair) | ||
| loop [action] [path] Goal loop controller (run, status, heartbeat, result, recover, configure, pause, resume) | ||
@@ -568,3 +568,2 @@ triage [action] [path] Triage inbox helpers (list, claim, promote) | ||
| ospec execute launch ./changes/active/onboarding-flow --target codex | ||
| ospec execute orchestrate ./changes/active/onboarding-flow --command "codex exec {{packet}}" --limit 2 --max-rounds 5 # fallback only | ||
| ospec execute complete task-1 ./changes/active/onboarding-flow --status DONE --summary "Implemented and verified" | ||
@@ -571,0 +570,0 @@ ospec execute complete task-1 ./changes/active/onboarding-flow --status DONE --usage-file ./usage.json |
@@ -458,2 +458,6 @@ "use strict"; | ||
| const archivePath = await this.resolveArchivePath(archivedRoot, featureState.feature, config); | ||
| const proposalPath = path.join(targetPath, constants_1.FILE_NAMES.PROPOSAL); | ||
| const originalProposal = await services_1.services.fileService.exists(proposalPath) | ||
| ? await services_1.services.fileService.readFile(proposalPath) | ||
| : null; | ||
| const nextState = { | ||
@@ -468,10 +472,47 @@ ...featureState, | ||
| await services_1.services.projectService.preflightArchivedKnowledgeWrite(projectRoot, archivePath); | ||
| await services_1.services.fileService.move(targetPath, archivePath); | ||
| await services_1.services.stateManager.writeState(archivePath, nextState); | ||
| await this.updateProposalStatus(archivePath, 'archived'); | ||
| await services_1.services.projectService.rebaseMovedChangeMarkdownLinks(targetPath, archivePath); | ||
| await services_1.services.projectService.archiveLinkedBrainstorms(projectRoot, featureState.feature, archivePath); | ||
| await services_1.services.projectService.rebuildIndex(projectRoot); | ||
| await services_1.services.projectService.assertArchivedKnowledgeIndexed(projectRoot, archivePath); | ||
| return this.toRelativePath(projectRoot, archivePath); | ||
| let moved = false; | ||
| let linksRebased = false; | ||
| try { | ||
| await services_1.services.fileService.move(targetPath, archivePath); | ||
| moved = true; | ||
| await services_1.services.stateManager.writeState(archivePath, nextState); | ||
| await this.updateProposalStatus(archivePath, 'archived'); | ||
| await services_1.services.projectService.rebaseMovedChangeMarkdownLinks(targetPath, archivePath); | ||
| linksRebased = true; | ||
| await services_1.services.projectService.rebuildIndex(projectRoot); | ||
| await services_1.services.projectService.assertArchivedKnowledgeIndexed(projectRoot, archivePath); | ||
| await services_1.services.projectService.archiveLinkedBrainstorms(projectRoot, featureState.feature, archivePath); | ||
| return this.toRelativePath(projectRoot, archivePath); | ||
| } | ||
| catch (error) { | ||
| if (!moved) | ||
| throw error; | ||
| const rollbackErrors = []; | ||
| try { | ||
| if (await services_1.services.fileService.exists(archivePath)) { | ||
| await services_1.services.fileService.move(archivePath, targetPath); | ||
| } | ||
| } | ||
| catch (rollbackError) { | ||
| rollbackErrors.push(`move: ${rollbackError?.message || rollbackError}`); | ||
| } | ||
| if (await services_1.services.fileService.exists(targetPath)) { | ||
| if (linksRebased) { | ||
| await services_1.services.projectService.rebaseMovedChangeMarkdownLinks(archivePath, targetPath) | ||
| .catch((rollbackError) => rollbackErrors.push(`links: ${rollbackError?.message || rollbackError}`)); | ||
| } | ||
| await services_1.services.stateManager.writeState(targetPath, featureState) | ||
| .catch((rollbackError) => rollbackErrors.push(`state: ${rollbackError?.message || rollbackError}`)); | ||
| if (originalProposal !== null) { | ||
| await services_1.services.fileService.writeFile(path.join(targetPath, constants_1.FILE_NAMES.PROPOSAL), originalProposal) | ||
| .catch((rollbackError) => rollbackErrors.push(`proposal: ${rollbackError?.message || rollbackError}`)); | ||
| } | ||
| } | ||
| await services_1.services.projectService.rebuildIndex(projectRoot) | ||
| .catch((rollbackError) => rollbackErrors.push(`index: ${rollbackError?.message || rollbackError}`)); | ||
| if (rollbackErrors.length > 0) { | ||
| throw new Error(`Archive failed (${error?.message || error}); rollback also failed: ${rollbackErrors.join('; ')}`); | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
@@ -478,0 +519,0 @@ inferProjectRootFromChangePath(startPath) { |
@@ -6,17 +6,5 @@ import { BaseCommand } from './BaseCommand'; | ||
| private tickPlan; | ||
| /** | ||
| * Session-bound in-process watcher (CLI-driven). Executes emitted fresh-context agent actions | ||
| * in parallel, then immediately observes their durable evidence. It waits only when no action | ||
| * is ready, and ends when the loop | ||
| * stops/pauses/finishes, a STOP sentinel appears, max ticks is reached, or the process exits | ||
| * (closing the session). It is NOT persistent — it dies with this process. | ||
| */ | ||
| private watch; | ||
| private parseIntervalMs; | ||
| private parseOptionalPath; | ||
| private parseFlagValue; | ||
| private parseMaxTicks; | ||
| private toAgentCliTarget; | ||
| private parseOptionalPositiveNumber; | ||
| private getActionTimeoutMs; | ||
| private resolveProjectRoot; | ||
@@ -23,0 +11,0 @@ private status; |
@@ -40,3 +40,2 @@ "use strict"; | ||
| const services_1 = require("../services"); | ||
| const AgentCliRunnerService_1 = require("../services/AgentCliRunnerService"); | ||
| const ProjectLayout_1 = require("../utils/ProjectLayout"); | ||
@@ -149,103 +148,5 @@ const BaseCommand_1 = require("./BaseCommand"); | ||
| } | ||
| /** | ||
| * Session-bound in-process watcher (CLI-driven). Executes emitted fresh-context agent actions | ||
| * in parallel, then immediately observes their durable evidence. It waits only when no action | ||
| * is ready, and ends when the loop | ||
| * stops/pauses/finishes, a STOP sentinel appears, max ticks is reached, or the process exits | ||
| * (closing the session). It is NOT persistent — it dies with this process. | ||
| */ | ||
| async watch(args) { | ||
| const inputPath = this.parseOptionalPath(args, ['--interval', '--max-ticks', '--timeout-ms', '--target'], ['--dry-run']); | ||
| const changePath = await this.resolveChangePath(inputPath); | ||
| const project = await this.resolveProjectRoot(changePath); | ||
| const config = await services_1.services.loopService.readConfig(changePath); | ||
| const intervalOverride = this.parseFlagValue(args, '--interval'); | ||
| const intervalLabel = intervalOverride || config.schedule.interval; | ||
| const intervalMs = this.parseIntervalMs(intervalLabel); | ||
| const maxTicks = this.parseMaxTicks(args); | ||
| const dryRun = args.includes('--dry-run'); | ||
| const timeoutMs = this.parseOptionalPositiveNumber(this.parseFlagValue(args, '--timeout-ms'), '--timeout-ms'); | ||
| const targetOverride = this.parseFlagValue(args, '--target'); | ||
| if (config.executionModel !== 'cli-driven') { | ||
| throw new Error('Loop watch is only available for cli-driven goals. Controller goals must execute each action through its selected runtime adapter.'); | ||
| } | ||
| if (targetOverride && targetOverride !== config.target) { | ||
| throw new Error(`Loop watch --target must match the persisted goal target (${config.target}); reconfigure the goal explicitly before changing executors.`); | ||
| } | ||
| const runner = (0, AgentCliRunnerService_1.createAgentCliRunnerService)(); | ||
| if (dryRun) { | ||
| const plan = await services_1.services.loopService.buildControllerTickPlan(changePath); | ||
| this.info('Loop watch dry-run: no tick, task dispatch, state update, or external process was executed.'); | ||
| for (const instruction of plan.instructions) | ||
| console.log(` - ${instruction}`); | ||
| return; | ||
| } | ||
| this.info(`Watching loop (session-bound, interval ${intervalLabel}, executor ${targetOverride || config.target}${dryRun ? ', dry-run' : ''}). Press Ctrl-C or close the session to stop.`); | ||
| let ticks = 0; | ||
| let active = true; | ||
| const stop = () => { active = false; }; | ||
| process.once('SIGINT', stop); | ||
| while (active) { | ||
| const result = await services_1.services.loopService.runOnce(changePath, { | ||
| trigger: 'watch', | ||
| projectRoot: project.projectRoot, | ||
| layoutConfig: project.config, | ||
| }); | ||
| ticks += 1; | ||
| console.log(`[tick ${ticks}] iteration=${result.iteration} status=${result.status} step=${result.currentStep} actions=${result.actions.length} tokens=${result.metrics.tokensUsed} no-progress=${result.metrics.noProgressCount}`); | ||
| if (result.stopped || result.status === 'done' || result.status === 'paused' || result.status === 'stopped') { | ||
| this.info(`Watch ending: loop status is ${result.status}.`); | ||
| break; | ||
| } | ||
| if (maxTicks !== null && ticks >= maxTicks) { | ||
| this.info(`Watch reached --max-ticks ${maxTicks}.`); | ||
| break; | ||
| } | ||
| if (result.actions.length > 0) { | ||
| const actionTimeoutMs = this.getActionTimeoutMs(config, result, timeoutMs); | ||
| const executions = await Promise.all(result.actions.map(async (action) => { | ||
| const target = this.toAgentCliTarget(targetOverride || action.target || config.target); | ||
| const executorId = `cli-watch:${process.pid}:${action.id}`; | ||
| await services_1.services.loopService.heartbeatExecution(changePath, { | ||
| actionItemId: action.id, | ||
| executorId, | ||
| leaseMs: actionTimeoutMs, | ||
| }); | ||
| const run = await runner.runAsync({ | ||
| target, | ||
| prompt: action.prompt, | ||
| dryRun: false, | ||
| timeoutMs: actionTimeoutMs, | ||
| cwd: project.projectRoot, | ||
| }); | ||
| console.log(`[action ${action.id}] target=${target} executed=${run.executed} exit=${run.exitCode ?? 'n/a'} timeout=${run.timedOut ? 'yes' : 'no'} truncated=${run.outputTruncated ? 'yes' : 'no'} duration=${run.durationMs}ms`); | ||
| const summary = run.executed | ||
| ? (run.exitCode === 0 ? `${action.id} completed; observing durable evidence.` : `${action.id} failed: ${run.stderr.trim().slice(0, 500)}`) | ||
| : `${action.id} was not executed${run.available ? ' (dry-run)' : ` (${run.command.bin} unavailable)`}.`; | ||
| return { | ||
| actionItemId: action.id, | ||
| executorId, | ||
| exitCode: run.exitCode, | ||
| timedOut: run.timedOut, | ||
| tokensUsed: 0, | ||
| summary, | ||
| }; | ||
| })); | ||
| await services_1.services.loopService.recordExecutionResults(changePath, executions); | ||
| continue; | ||
| } | ||
| await new Promise(resolve => setTimeout(resolve, intervalMs)); | ||
| } | ||
| process.removeListener('SIGINT', stop); | ||
| async watch(_args) { | ||
| throw new Error('Loop watch agent execution was removed. Use "ospec loop run --once --json" and dispatch each action through the current model harness native subagent API.'); | ||
| } | ||
| parseIntervalMs(interval) { | ||
| const match = String(interval || '').trim().match(/^(\d+)\s*(ms|s|m|h)?$/i); | ||
| if (!match) { | ||
| return 600000; | ||
| } | ||
| const value = Number(match[1]); | ||
| const unit = (match[2] || 'm').toLowerCase(); | ||
| const factor = unit === 'ms' ? 1 : unit === 's' ? 1000 : unit === 'h' ? 3600000 : 60000; | ||
| return Math.max(1, value * factor); | ||
| } | ||
| parseOptionalPath(args, valueFlags, booleanFlags) { | ||
@@ -286,38 +187,2 @@ const valueSet = new Set(valueFlags); | ||
| } | ||
| parseMaxTicks(args) { | ||
| for (let index = 0; index < args.length; index += 1) { | ||
| if (args[index] === '--max-ticks') { | ||
| return Math.max(1, Number(args[index + 1]) || 1); | ||
| } | ||
| if (args[index].startsWith('--max-ticks=')) { | ||
| return Math.max(1, Number(args[index].slice('--max-ticks='.length)) || 1); | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| toAgentCliTarget(target) { | ||
| if (target === 'claude' || target === 'codex' || target === 'gpt') | ||
| return target; | ||
| throw new Error(`Loop watch cannot execute target "${target}" directly. Configure claude/codex/gpt or use controller mode with the harness-native ${target} subagent adapter.`); | ||
| } | ||
| parseOptionalPositiveNumber(value, flag) { | ||
| if (value === undefined) | ||
| return undefined; | ||
| const parsed = Number(value); | ||
| if (!Number.isFinite(parsed) || parsed <= 0) | ||
| throw new Error(`${flag} must be a positive number.`); | ||
| return parsed; | ||
| } | ||
| getActionTimeoutMs(config, result, requested) { | ||
| const limits = []; | ||
| if (requested !== undefined) | ||
| limits.push(requested); | ||
| if (config.stopConditions.budgetMinutes !== null) { | ||
| limits.push(Math.max(1, (config.stopConditions.budgetMinutes - result.metrics.elapsedMinutes) * 60000)); | ||
| } | ||
| if (config.stopConditions.expiresAt) { | ||
| limits.push(Math.max(1, Date.parse(config.stopConditions.expiresAt) - Date.now())); | ||
| } | ||
| return limits.length > 0 ? Math.floor(Math.min(...limits)) : undefined; | ||
| } | ||
| async resolveProjectRoot(changePath) { | ||
@@ -452,5 +317,5 @@ let current = path.resolve(changePath); | ||
| if (model) { | ||
| if (model !== 'controller' && model !== 'cli-driven') | ||
| throw new Error('--execution-model must be controller or cli-driven.'); | ||
| options.executionModel = model; | ||
| if (model !== 'controller') | ||
| throw new Error('--execution-model only supports controller; agent CLI execution was removed.'); | ||
| options.executionModel = 'controller'; | ||
| } | ||
@@ -457,0 +322,0 @@ const harnessInteractive = scalar('--harness-interactive'); |
@@ -27,3 +27,10 @@ import { BaseCommand } from './BaseCommand'; | ||
| execute(featureName: string, rootDir?: string, options?: NewCommandOptions): Promise<void>; | ||
| private acquireChangeCreationLease; | ||
| private releaseChangeCreationLease; | ||
| private readChangeCreationLockOwner; | ||
| private isProcessAlive; | ||
| private refreshChangeCreationLockIfOwned; | ||
| private removeChangeCreationLockIfOwned; | ||
| private removeCorruptChangeCreationLockIfUnchanged; | ||
| private normalizeFlags; | ||
| } |
@@ -38,2 +38,4 @@ "use strict"; | ||
| const path = __importStar(require("path")); | ||
| const crypto_1 = require("crypto"); | ||
| const fs_1 = require("fs"); | ||
| const constants_1 = require("../core/constants"); | ||
@@ -48,4 +50,13 @@ const services_1 = require("../services"); | ||
| const SessionCommand_1 = require("./SessionCommand"); | ||
| const CHANGE_CREATION_LOCK_FILE = '.change-creation.lock'; | ||
| const CHANGE_CREATION_LOCK_TIMEOUT_MS = 30 * 1000; | ||
| const STALE_CHANGE_CREATION_LOCK_MS = 2 * 60 * 1000; | ||
| const CHANGE_CREATION_LOCK_HEARTBEAT_MS = 30 * 1000; | ||
| class NewCommand extends BaseCommand_1.BaseCommand { | ||
| async execute(featureName, rootDir, options = {}) { | ||
| let creationLease = null; | ||
| let createdFeatureDir = null; | ||
| let rollbackProjectRoot = null; | ||
| let rollbackPlacement = null; | ||
| let sessionBriefBackup = null; | ||
| try { | ||
@@ -61,6 +72,19 @@ this.validateArgs([featureName], 1); | ||
| this.logger.info(`Creating ${placement === constants_1.DIR_NAMES.QUEUED ? 'queued change' : 'change'}: ${featureName}`); | ||
| creationLease = await this.acquireChangeCreationLease(targetDir, config); | ||
| if (placement === constants_1.DIR_NAMES.ACTIVE) { | ||
| const sessionDir = path.join(targetDir, '.ospec'); | ||
| const jsonPath = path.join(sessionDir, 'session-brief.json'); | ||
| const markdownPath = path.join(sessionDir, 'session-brief.md'); | ||
| sessionBriefBackup = { | ||
| json: await services_1.services.fileService.exists(jsonPath) ? await services_1.services.fileService.readFile(jsonPath) : null, | ||
| markdown: await services_1.services.fileService.exists(markdownPath) ? await services_1.services.fileService.readFile(markdownPath) : null, | ||
| }; | ||
| } | ||
| await this.ensureChangeNameAvailable(targetDir, featureName, config); | ||
| await this.ensureSingleActiveMode(targetDir, placement, featureName, config); | ||
| await services_1.services.fileService.ensureDir((0, ProjectLayout_1.resolveManagedPath)(targetDir, `${constants_1.DIR_NAMES.CHANGES}/${placement}`, config)); | ||
| await services_1.services.fileService.ensureDir(featureDir); | ||
| await fs_1.promises.mkdir(featureDir); | ||
| createdFeatureDir = featureDir; | ||
| rollbackProjectRoot = targetDir; | ||
| rollbackPlacement = placement; | ||
| const composer = new PluginWorkflowComposer_1.PluginWorkflowComposer(config); | ||
@@ -199,5 +223,2 @@ const flags = this.normalizeFlags(options.flags); | ||
| } | ||
| else if (loopConfig.executionModel === 'cli-driven') { | ||
| this.info(' CLI-driven execution was explicitly selected. The IDE controller will not dispatch native subagents.'); | ||
| } | ||
| else { | ||
@@ -217,8 +238,138 @@ this.warn(` IDE controller blocked: target=${loopConfig.target}, interactive=${loopConfig.capability?.interactive ?? false}, nativeSubagents=${loopConfig.capability?.nativeSubagentCapability ?? 'unknown'}. Report the current harness capabilities explicitly before starting executable Loop actions.`); | ||
| } | ||
| createdFeatureDir = null; | ||
| } | ||
| catch (error) { | ||
| if (createdFeatureDir) { | ||
| await services_1.services.fileService.remove(createdFeatureDir).catch((rollbackError) => { | ||
| this.warn(`Failed to roll back partial change ${createdFeatureDir}: ${rollbackError?.message || rollbackError}`); | ||
| }); | ||
| if (rollbackProjectRoot | ||
| && rollbackPlacement === constants_1.DIR_NAMES.ACTIVE | ||
| && sessionBriefBackup) { | ||
| const sessionDir = path.join(rollbackProjectRoot, '.ospec'); | ||
| for (const [fileName, content] of [ | ||
| ['session-brief.json', sessionBriefBackup.json], | ||
| ['session-brief.md', sessionBriefBackup.markdown], | ||
| ]) { | ||
| const filePath = path.join(sessionDir, fileName); | ||
| if (content === null) | ||
| await services_1.services.fileService.remove(filePath).catch(() => undefined); | ||
| else | ||
| await services_1.services.fileService.writeFile(filePath, content).catch(() => undefined); | ||
| } | ||
| } | ||
| } | ||
| this.error(`Failed to create change: ${error}`); | ||
| throw error; | ||
| } | ||
| finally { | ||
| if (creationLease) | ||
| await this.releaseChangeCreationLease(creationLease); | ||
| } | ||
| } | ||
| async acquireChangeCreationLease(targetDir, config) { | ||
| const changesRoot = (0, ProjectLayout_1.resolveManagedPath)(targetDir, constants_1.DIR_NAMES.CHANGES, config); | ||
| await services_1.services.fileService.ensureDir(changesRoot); | ||
| const lockPath = path.join(changesRoot, CHANGE_CREATION_LOCK_FILE); | ||
| const nonce = (0, crypto_1.randomBytes)(16).toString('hex'); | ||
| const startedAt = Date.now(); | ||
| while (true) { | ||
| try { | ||
| const handle = await fs_1.promises.open(lockPath, 'wx'); | ||
| await handle.writeFile(JSON.stringify({ | ||
| version: 2, | ||
| pid: process.pid, | ||
| acquiredAt: new Date().toISOString(), | ||
| nonce, | ||
| heartbeat: true, | ||
| })); | ||
| const heartbeat = setInterval(() => { | ||
| void this.refreshChangeCreationLockIfOwned(lockPath, nonce); | ||
| }, CHANGE_CREATION_LOCK_HEARTBEAT_MS); | ||
| heartbeat.unref(); | ||
| return { lockPath, nonce, handle, heartbeat }; | ||
| } | ||
| catch (error) { | ||
| if (error?.code !== 'EEXIST') | ||
| throw error; | ||
| const owner = await this.readChangeCreationLockOwner(lockPath); | ||
| const stat = await fs_1.promises.stat(lockPath).catch(() => null); | ||
| const lockAgeMs = stat ? Date.now() - stat.mtimeMs : 0; | ||
| if (stat | ||
| && (lockAgeMs >= STALE_CHANGE_CREATION_LOCK_MS || lockAgeMs <= -STALE_CHANGE_CREATION_LOCK_MS) | ||
| && (owner | ||
| ? ((!this.isProcessAlive(owner.pid) || owner.heartbeat) | ||
| && await this.removeChangeCreationLockIfOwned(lockPath, owner.nonce)) | ||
| : await this.removeCorruptChangeCreationLockIfUnchanged(lockPath, stat))) | ||
| continue; | ||
| if (Date.now() - startedAt >= CHANGE_CREATION_LOCK_TIMEOUT_MS) { | ||
| throw new Error(`Timed out waiting for change creation lease at ${lockPath}.`); | ||
| } | ||
| await new Promise(resolve => setTimeout(resolve, 50)); | ||
| } | ||
| } | ||
| } | ||
| async releaseChangeCreationLease(lease) { | ||
| clearInterval(lease.heartbeat); | ||
| await lease.handle.close().catch(() => undefined); | ||
| await this.removeChangeCreationLockIfOwned(lease.lockPath, lease.nonce); | ||
| } | ||
| async readChangeCreationLockOwner(lockPath) { | ||
| try { | ||
| const value = JSON.parse(await fs_1.promises.readFile(lockPath, 'utf8')); | ||
| return Number.isInteger(value?.pid) && value.pid > 0 && typeof value?.nonce === 'string' && value.nonce.length > 0 | ||
| ? { pid: value.pid, nonce: value.nonce, heartbeat: value?.version === 2 && value?.heartbeat === true } | ||
| : null; | ||
| } | ||
| catch { | ||
| return null; | ||
| } | ||
| } | ||
| isProcessAlive(pid) { | ||
| try { | ||
| process.kill(pid, 0); | ||
| return true; | ||
| } | ||
| catch (error) { | ||
| return error?.code !== 'ESRCH' && error?.code !== 'EINVAL'; | ||
| } | ||
| } | ||
| async refreshChangeCreationLockIfOwned(lockPath, nonce) { | ||
| const owner = await this.readChangeCreationLockOwner(lockPath); | ||
| if (owner?.nonce !== nonce) | ||
| return; | ||
| const now = new Date(); | ||
| await fs_1.promises.utimes(lockPath, now, now).catch(() => undefined); | ||
| } | ||
| async removeChangeCreationLockIfOwned(lockPath, nonce) { | ||
| const owner = await this.readChangeCreationLockOwner(lockPath); | ||
| if (owner?.nonce !== nonce) | ||
| return false; | ||
| try { | ||
| await fs_1.promises.unlink(lockPath); | ||
| return true; | ||
| } | ||
| catch (error) { | ||
| if (error?.code === 'ENOENT') | ||
| return false; | ||
| throw error; | ||
| } | ||
| } | ||
| async removeCorruptChangeCreationLockIfUnchanged(lockPath, observed) { | ||
| const current = await fs_1.promises.stat(lockPath).catch(() => null); | ||
| if (!current | ||
| || current.size !== observed.size | ||
| || current.mtimeMs !== observed.mtimeMs | ||
| || await this.readChangeCreationLockOwner(lockPath)) | ||
| return false; | ||
| try { | ||
| await fs_1.promises.unlink(lockPath); | ||
| return true; | ||
| } | ||
| catch (error) { | ||
| if (error?.code === 'ENOENT') | ||
| return false; | ||
| throw error; | ||
| } | ||
| } | ||
| async resolveDocumentLanguage(targetDir, config) { | ||
@@ -225,0 +376,0 @@ const configLanguage = this.normalizeDocumentLanguage(config?.documentLanguage); |
@@ -501,3 +501,3 @@ "use strict"; | ||
| startupUse: 'Use using-ospec.md as repository instruction context and run the session command manually when needed.', | ||
| nativeExecution: 'Use Copilot task context from launch-plan.md; keep CLI worker runners as fallback only.', | ||
| nativeExecution: 'Use the Copilot native task context from launch-plan.md; stop if native subagents are unavailable.', | ||
| }, | ||
@@ -504,0 +504,0 @@ { |
| /** | ||
| * Builds and (optionally) runs external agent-CLI commands for the cli-driven execution model | ||
| * (Execution-Model Contract 1/2). Intentionally separate from `RunService` (a QueueRun manager). | ||
| * | ||
| * Command forms are exact and verified by tests: | ||
| * - claude -> `claude -p "<prompt>"` (print mode; NOT `--goal`, which does not exist) | ||
| * - codex -> `codex exec "<prompt>"` (`/goal` is an interactive slash; non-interactive is `exec`) | ||
| * | ||
| * Default behavior is dry-run: the command is returned/printed, never executed, unless `run: true`. | ||
| * @deprecated Agent CLI processes were removed from OSpec execution. The | ||
| * current model harness must dispatch its own native subagents. | ||
| */ | ||
| export type AgentCliTarget = 'claude' | 'codex' | 'gpt'; | ||
| export interface AgentCliCommand { | ||
| /** Resolved binary name. */ | ||
| bin: string; | ||
| /** Argument vector (excluding the binary). */ | ||
| args: string[]; | ||
| /** Human-readable command string for logs / dry-run output. */ | ||
| display: string; | ||
@@ -43,22 +34,10 @@ } | ||
| } | ||
| /** @deprecated Use the runtime adapter nativeSubagent contract. */ | ||
| export declare class AgentCliRunnerService { | ||
| /** | ||
| * Build the exact external command for a target. Pure; never emits `claude --goal`. | ||
| */ | ||
| buildCommand(target: AgentCliTarget, prompt: string): AgentCliCommand; | ||
| /** Detect whether a CLI binary is on PATH (cross-platform). */ | ||
| isAvailable(bin: string): boolean; | ||
| /** | ||
| * Resolve the command for a target+prompt and, unless dry-run, execute it. | ||
| * Detection failures and dry-run both return without throwing. | ||
| */ | ||
| run(options: AgentCliRunOptions): AgentCliRunResult; | ||
| /** | ||
| * Asynchronously run an external agent in a fresh process. The binary and argument vector are | ||
| * passed directly to spawn, so prompt text is never interpreted by a shell. | ||
| */ | ||
| runAsync(options: AgentCliRunOptions): Promise<AgentCliAsyncRunResult>; | ||
| private terminateProcessTree; | ||
| private quote; | ||
| buildCommand(_target: AgentCliTarget, _prompt: string): AgentCliCommand; | ||
| isAvailable(_bin: string): boolean; | ||
| run(_options: AgentCliRunOptions): AgentCliRunResult; | ||
| runAsync(_options: AgentCliRunOptions): Promise<AgentCliAsyncRunResult>; | ||
| } | ||
| /** @deprecated Use RuntimeExecutionAdapterService. */ | ||
| export declare function createAgentCliRunnerService(): AgentCliRunnerService; |
| "use strict"; | ||
| /** | ||
| * @deprecated Agent CLI processes were removed from OSpec execution. The | ||
| * current model harness must dispatch its own native subagents. | ||
| */ | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.AgentCliRunnerService = void 0; | ||
| exports.createAgentCliRunnerService = createAgentCliRunnerService; | ||
| const child_process_1 = require("child_process"); | ||
| const DEFAULT_AGENT_TIMEOUT_MS = 30 * 60 * 1000; | ||
| const DEFAULT_MAX_OUTPUT_BYTES = 1024 * 1024; | ||
| const REMOVAL_MESSAGE = 'Agent CLI execution was removed. Dispatch this packet through the current model harness native subagent API.'; | ||
| /** @deprecated Use the runtime adapter nativeSubagent contract. */ | ||
| class AgentCliRunnerService { | ||
| /** | ||
| * Build the exact external command for a target. Pure; never emits `claude --goal`. | ||
| */ | ||
| buildCommand(target, prompt) { | ||
| if (target === 'claude') { | ||
| return { bin: 'claude', args: ['-p', prompt], display: `claude -p ${this.quote(prompt)}` }; | ||
| } | ||
| // codex and gpt both use the Codex CLI non-interactive entrypoint. | ||
| return { bin: 'codex', args: ['exec', prompt], display: `codex exec ${this.quote(prompt)}` }; | ||
| buildCommand(_target, _prompt) { | ||
| throw new Error(REMOVAL_MESSAGE); | ||
| } | ||
| /** Detect whether a CLI binary is on PATH (cross-platform). */ | ||
| isAvailable(bin) { | ||
| const probe = process.platform === 'win32' | ||
| ? (0, child_process_1.spawnSync)('where', [bin], { encoding: 'utf8' }) | ||
| : (0, child_process_1.spawnSync)('command', ['-v', bin], { encoding: 'utf8', shell: true }); | ||
| return probe.status === 0 && String(probe.stdout || '').trim().length > 0; | ||
| isAvailable(_bin) { | ||
| return false; | ||
| } | ||
| /** | ||
| * Resolve the command for a target+prompt and, unless dry-run, execute it. | ||
| * Detection failures and dry-run both return without throwing. | ||
| */ | ||
| run(options) { | ||
| const command = this.buildCommand(options.target, options.prompt); | ||
| const dryRun = options.dryRun !== false; // default dry-run | ||
| const available = this.isAvailable(command.bin); | ||
| if (dryRun || !available) { | ||
| return { command, dryRun, executed: false, available, exitCode: null, stdout: '', stderr: '' }; | ||
| } | ||
| const result = (0, child_process_1.spawnSync)(command.bin, command.args, { | ||
| encoding: 'utf8', | ||
| timeout: options.timeoutMs, | ||
| cwd: options.cwd, | ||
| env: options.env ? { ...process.env, ...options.env } : process.env, | ||
| }); | ||
| return { | ||
| command, | ||
| dryRun: false, | ||
| executed: true, | ||
| available: true, | ||
| exitCode: typeof result.status === 'number' ? result.status : null, | ||
| stdout: String(result.stdout || ''), | ||
| stderr: String(result.stderr || ''), | ||
| }; | ||
| run(_options) { | ||
| throw new Error(REMOVAL_MESSAGE); | ||
| } | ||
| /** | ||
| * Asynchronously run an external agent in a fresh process. The binary and argument vector are | ||
| * passed directly to spawn, so prompt text is never interpreted by a shell. | ||
| */ | ||
| async runAsync(options) { | ||
| const startedAt = Date.now(); | ||
| const command = this.buildCommand(options.target, options.prompt); | ||
| const dryRun = options.dryRun !== false; | ||
| const available = this.isAvailable(command.bin); | ||
| if (dryRun || !available) { | ||
| return { | ||
| command, | ||
| dryRun, | ||
| executed: false, | ||
| available, | ||
| exitCode: null, | ||
| stdout: '', | ||
| stderr: '', | ||
| durationMs: Math.max(0, Date.now() - startedAt), | ||
| timedOut: false, | ||
| outputTruncated: false, | ||
| }; | ||
| } | ||
| return new Promise(resolve => { | ||
| const child = (0, child_process_1.spawn)(command.bin, command.args, { | ||
| cwd: options.cwd, | ||
| env: options.env ? { ...process.env, ...options.env } : process.env, | ||
| shell: false, | ||
| stdio: ['ignore', 'pipe', 'pipe'], | ||
| detached: process.platform !== 'win32', | ||
| windowsHide: true, | ||
| }); | ||
| const stdoutChunks = []; | ||
| const stderrChunks = []; | ||
| let capturedBytes = 0; | ||
| let outputTruncated = false; | ||
| let timedOut = false; | ||
| let spawnError = null; | ||
| const timeoutMs = typeof options.timeoutMs === 'number' && options.timeoutMs > 0 | ||
| ? options.timeoutMs | ||
| : DEFAULT_AGENT_TIMEOUT_MS; | ||
| const maxOutputBytes = typeof options.maxOutputBytes === 'number' && options.maxOutputBytes > 0 | ||
| ? Math.floor(options.maxOutputBytes) | ||
| : DEFAULT_MAX_OUTPUT_BYTES; | ||
| const capture = (target, chunk) => { | ||
| const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)); | ||
| const remaining = Math.max(0, maxOutputBytes - capturedBytes); | ||
| if (remaining > 0) | ||
| target.push(buffer.subarray(0, remaining)); | ||
| capturedBytes += Math.min(buffer.length, remaining); | ||
| if (buffer.length > remaining) | ||
| outputTruncated = true; | ||
| }; | ||
| const timeout = setTimeout(() => { | ||
| timedOut = true; | ||
| this.terminateProcessTree(child.pid, () => child.kill()); | ||
| }, timeoutMs); | ||
| child.stdout?.on('data', chunk => { | ||
| capture(stdoutChunks, chunk); | ||
| }); | ||
| child.stderr?.on('data', chunk => { | ||
| capture(stderrChunks, chunk); | ||
| }); | ||
| child.once('error', error => { | ||
| spawnError = error; | ||
| }); | ||
| child.once('close', exitCode => { | ||
| clearTimeout(timeout); | ||
| const truncationMarker = outputTruncated ? '\n[output truncated]' : ''; | ||
| const stdout = `${Buffer.concat(stdoutChunks).toString('utf8')}${truncationMarker}`; | ||
| const capturedStderr = Buffer.concat(stderrChunks).toString('utf8'); | ||
| resolve({ | ||
| command, | ||
| dryRun: false, | ||
| executed: true, | ||
| available: true, | ||
| exitCode: typeof exitCode === 'number' ? exitCode : null, | ||
| stdout, | ||
| stderr: spawnError | ||
| ? `${capturedStderr}${capturedStderr && !capturedStderr.endsWith('\n') ? '\n' : ''}${spawnError.message}` | ||
| : capturedStderr, | ||
| durationMs: Math.max(0, Date.now() - startedAt), | ||
| timedOut, | ||
| outputTruncated, | ||
| }); | ||
| }); | ||
| }); | ||
| async runAsync(_options) { | ||
| throw new Error(REMOVAL_MESSAGE); | ||
| } | ||
| terminateProcessTree(pid, fallback) { | ||
| if (!pid) { | ||
| fallback(); | ||
| return; | ||
| } | ||
| try { | ||
| if (process.platform === 'win32') { | ||
| const killer = (0, child_process_1.spawn)('taskkill', ['/pid', String(pid), '/T', '/F'], { | ||
| stdio: 'ignore', | ||
| windowsHide: true, | ||
| }); | ||
| killer.once('error', fallback); | ||
| return; | ||
| } | ||
| process.kill(-pid, 'SIGTERM'); | ||
| } | ||
| catch { | ||
| fallback(); | ||
| } | ||
| } | ||
| quote(value) { | ||
| if (/^[A-Za-z0-9_./:@-]+$/.test(value)) { | ||
| return value; | ||
| } | ||
| return `"${value.replace(/"/g, '\\"')}"`; | ||
| } | ||
| } | ||
| exports.AgentCliRunnerService = AgentCliRunnerService; | ||
| /** @deprecated Use RuntimeExecutionAdapterService. */ | ||
| function createAgentCliRunnerService() { | ||
| return new AgentCliRunnerService(); | ||
| } |
@@ -13,2 +13,4 @@ /** | ||
| export interface HarnessCapability { | ||
| /** Model harness target that reported this session-bound capability. */ | ||
| target: string; | ||
| /** Whether the requested primitive maps to a confirmed native harness primitive. */ | ||
@@ -15,0 +17,0 @@ nativeLoopCapability: NativeLoopCapability; |
@@ -68,2 +68,3 @@ "use strict"; | ||
| return { | ||
| target, | ||
| nativeLoopCapability: nativeSubagentCapability, | ||
@@ -99,5 +100,6 @@ probeSource, | ||
| if (!controllerAvailable) { | ||
| warnings.push(`Controller dispatch is unavailable: interactive=${interactive}, nativeSubagents=${nativeSubagentCapability}. Report explicit harness capabilities or opt into CLI-driven execution.`); | ||
| warnings.push(`Controller dispatch is unavailable: interactive=${interactive}, nativeSubagents=${nativeSubagentCapability}. Report explicit capabilities from the current model harness; agent CLI fallback is not supported.`); | ||
| } | ||
| return { | ||
| target, | ||
| nativeLoopCapability: capability, | ||
@@ -104,0 +106,0 @@ probeSource, |
@@ -9,2 +9,3 @@ import { FileService } from './FileService'; | ||
| export type LoopStatus = 'idle' | 'running' | 'paused' | 'stopped' | 'done'; | ||
| /** `cli-driven` is retained only so callers can receive a migration error. */ | ||
| export type LoopExecutionModel = 'controller' | 'cli-driven'; | ||
@@ -58,2 +59,4 @@ export type LoopActionKind = 'implementation' | 'task-review' | 'final-review' | 'verification' | 'legacy'; | ||
| controllerProvenanceRequired?: boolean; | ||
| nativeSessionTarget?: string; | ||
| nativeSessionReportedAt?: string; | ||
| } | ||
@@ -207,5 +210,4 @@ export interface PendingControllerAction { | ||
| /** | ||
| * Durable plan-act-observe controller for goal task graphs. OSpec still does not invoke a | ||
| * harness-native subagent itself: controller mode emits bounded action packets, while CLI watch | ||
| * executes those packets through AgentCliRunnerService and records the result for the next tick. | ||
| * Durable plan-act-observe controller for goal task graphs. OSpec emits bounded action packets; | ||
| * the active model harness executes them through its native subagent primitive. | ||
| */ | ||
@@ -265,2 +267,3 @@ export declare class LoopService { | ||
| private readControllerLockOwner; | ||
| private refreshControllerLockIfOwned; | ||
| private isProcessAlive; | ||
@@ -299,2 +302,3 @@ private removeControllerLockIfOwned; | ||
| private isControllerCapabilityCurrent; | ||
| private assertActionNativeSession; | ||
| private resolveRuntimeAdapter; | ||
@@ -301,0 +305,0 @@ private getBudgetedBatchLimit; |
@@ -1,4 +0,8 @@ | ||
| import type { HarnessCapability } from './CapabilityProbeService'; | ||
| export type RuntimeExecutionAdapterPreference = 'auto' | 'orca' | 'native' | 'cli' | 'generic'; | ||
| export type RuntimeExecutionAdapterKind = 'orca' | 'native' | 'cli' | 'generic'; | ||
| /** | ||
| * `orca`, `cli`, and `generic` are retained as legacy preference inputs so an | ||
| * existing environment receives a migration diagnostic instead of an unknown | ||
| * value. New resolutions are always native-only. | ||
| */ | ||
| export type RuntimeExecutionAdapterPreference = 'auto' | 'native' | 'orca' | 'cli' | 'generic'; | ||
| export type RuntimeExecutionAdapterKind = 'native' | 'orca' | 'cli' | 'generic'; | ||
| export interface RuntimeExecutionAdapterCommand { | ||
@@ -9,2 +13,9 @@ bin: string; | ||
| } | ||
| export interface RuntimeNativeSubagentContract { | ||
| target: string; | ||
| primitive: string; | ||
| dispatch: string; | ||
| wait: string; | ||
| result: string; | ||
| } | ||
| export interface RuntimeExecutionAdapterCandidate { | ||
@@ -21,2 +32,3 @@ id: string; | ||
| commandTemplates: RuntimeExecutionAdapterCommand[]; | ||
| nativeSubagent?: RuntimeNativeSubagentContract | null; | ||
| } | ||
@@ -44,2 +56,3 @@ export interface RuntimeExecutionAdapterResolution { | ||
| now?: Date; | ||
| /** @deprecated Native adapter resolution is deterministic and no longer writes a probe cache. */ | ||
| cacheFilePath?: string; | ||
@@ -53,2 +66,3 @@ } | ||
| } | ||
| /** @deprecated Runtime adapter resolution never invokes commands. */ | ||
| export type RuntimeAdapterCommandRunner = (bin: string, args: string[], options: { | ||
@@ -60,27 +74,20 @@ cwd: string; | ||
| /** | ||
| * Resolves the concrete worker adapter for the current runtime. Detection is capability-based: | ||
| * an Orca process name is never sufficient without a callable CLI and current-worktree proof. | ||
| * Resolves the current model harness native subagent contract. | ||
| * | ||
| * OSpec deliberately does not probe Orca, PATH binaries, or agent CLIs. A | ||
| * session-bound capability assertion from the active model harness is the only | ||
| * authority that can make an adapter executable. | ||
| */ | ||
| import type { HarnessCapability } from './CapabilityProbeService'; | ||
| export declare class RuntimeExecutionAdapterService { | ||
| private readonly commandRunner; | ||
| private readonly platform; | ||
| private readonly pathExists; | ||
| private readonly resolutionCache; | ||
| constructor(commandRunner?: RuntimeAdapterCommandRunner, platform?: NodeJS.Platform, pathExists?: (candidate: string) => boolean); | ||
| /** | ||
| * The constructor parameters remain for source compatibility with 1.8.1 | ||
| * callers that injected probe functions. They are intentionally ignored. | ||
| */ | ||
| constructor(_commandRunner?: RuntimeAdapterCommandRunner, _platform?: NodeJS.Platform, _pathExists?: (candidate: string) => boolean); | ||
| resolve(input: RuntimeExecutionAdapterResolveInput): RuntimeExecutionAdapterResolution; | ||
| private probeOrca; | ||
| private probeNative; | ||
| private probeTargetCli; | ||
| private buildGenericCandidate; | ||
| private unavailable; | ||
| private isCommandAvailable; | ||
| private runCommand; | ||
| private commandTemplate; | ||
| private resolveInvocation; | ||
| private pathApi; | ||
| private cacheKey; | ||
| private readPersistentCache; | ||
| private writePersistentCache; | ||
| private buildNativeCandidate; | ||
| private getAvailabilityReason; | ||
| } | ||
| export declare function createRuntimeExecutionAdapterService(): RuntimeExecutionAdapterService; | ||
| export {}; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.RuntimeExecutionAdapterService = void 0; | ||
| exports.createRuntimeExecutionAdapterService = createRuntimeExecutionAdapterService; | ||
| const path = __importStar(require("path")); | ||
| const child_process_1 = require("child_process"); | ||
| const fs_1 = require("fs"); | ||
| const COMMAND_TIMEOUT_MS = 2500; | ||
| const RESOLUTION_CACHE_TTL_MS = 30000; | ||
| const PERSISTENT_CACHE_VERSION = '2.0'; | ||
| const NATIVE_TARGETS = Object.freeze({ | ||
| codex: { | ||
| primitive: 'spawn_agent', | ||
| dispatch: 'Call spawn_agent once per action packet in the current Codex session.', | ||
| wait: 'Use wait_agent for the issued batch and keep child ownership bound to its returned id.', | ||
| result: 'Record heartbeat and result evidence with the real Codex child id.', | ||
| }, | ||
| gpt: { | ||
| primitive: 'spawn_agent', | ||
| dispatch: 'Call spawn_agent once per action packet in the current GPT/Codex session.', | ||
| wait: 'Use wait_agent for the issued batch and keep child ownership bound to its returned id.', | ||
| result: 'Record heartbeat and result evidence with the real GPT/Codex child id.', | ||
| }, | ||
| claude: { | ||
| primitive: 'Task', | ||
| dispatch: 'Call the Claude Task tool once per action packet with a fresh subagent context.', | ||
| wait: 'Wait for the native Task batch in the current Claude session.', | ||
| result: 'Record heartbeat and result evidence with the real Claude Task id.', | ||
| }, | ||
| gemini: { | ||
| primitive: '@generalist', | ||
| dispatch: 'Dispatch one Gemini @generalist subagent per action packet.', | ||
| wait: 'Wait for the native @generalist batch in the current Gemini session.', | ||
| result: 'Record heartbeat and result evidence with the real Gemini child id.', | ||
| }, | ||
| opencode: { | ||
| primitive: '@mention', | ||
| dispatch: 'Dispatch one OpenCode @mention subagent per action packet.', | ||
| wait: 'Wait for the native @mention batch in the current OpenCode session.', | ||
| result: 'Record heartbeat and result evidence with the real OpenCode child id.', | ||
| }, | ||
| cursor: { | ||
| primitive: 'Agent', | ||
| dispatch: 'Use the current Cursor session native Agent/task primitive once per action packet.', | ||
| wait: 'Wait for the native Cursor child batch in the current session.', | ||
| result: 'Record heartbeat and result evidence with the real Cursor child id.', | ||
| }, | ||
| copilot: { | ||
| primitive: 'Agent', | ||
| dispatch: 'Use the current Copilot session native agent/task primitive once per action packet.', | ||
| wait: 'Wait for the native Copilot child batch in the current session.', | ||
| result: 'Record heartbeat and result evidence with the real Copilot child id.', | ||
| }, | ||
| }); | ||
| /** | ||
| * Resolves the concrete worker adapter for the current runtime. Detection is capability-based: | ||
| * an Orca process name is never sufficient without a callable CLI and current-worktree proof. | ||
| * Resolves the current model harness native subagent contract. | ||
| * | ||
| * OSpec deliberately does not probe Orca, PATH binaries, or agent CLIs. A | ||
| * session-bound capability assertion from the active model harness is the only | ||
| * authority that can make an adapter executable. | ||
| */ | ||
| class RuntimeExecutionAdapterService { | ||
| constructor(commandRunner = defaultCommandRunner, platform = process.platform, pathExists = fs_1.existsSync) { | ||
| this.commandRunner = commandRunner; | ||
| this.platform = platform; | ||
| this.pathExists = pathExists; | ||
| this.resolutionCache = new Map(); | ||
| } | ||
| /** | ||
| * The constructor parameters remain for source compatibility with 1.8.1 | ||
| * callers that injected probe functions. They are intentionally ignored. | ||
| */ | ||
| constructor(_commandRunner, _platform, _pathExists) { } | ||
| resolve(input) { | ||
| const env = input.env ?? process.env; | ||
| const target = String(input.target || 'generic').trim().toLowerCase() || 'generic'; | ||
| const target = normalizeTarget(input.target); | ||
| const preference = normalizePreference(input.preference ?? env.OSPEC_EXECUTION_ADAPTER); | ||
| const strict = input.strict ?? normalizeBoolean(env.OSPEC_EXECUTION_ADAPTER_STRICT) ?? false; | ||
| const projectRoot = this.pathApi().resolve(input.projectRoot); | ||
| const now = input.now ?? new Date(); | ||
| const cacheKey = this.cacheKey({ | ||
| projectRoot, | ||
| target, | ||
| preference, | ||
| strict, | ||
| independent: input.requiresIndependentWorker === true, | ||
| capability: input.capability, | ||
| env, | ||
| }); | ||
| const cached = this.resolutionCache.get(cacheKey); | ||
| if (cached && cached.expiresAt > now.getTime()) | ||
| return cached.resolution; | ||
| if (cached) | ||
| this.resolutionCache.delete(cacheKey); | ||
| const persistent = this.readPersistentCache(input.cacheFilePath, cacheKey, now); | ||
| if (persistent) { | ||
| this.resolutionCache.set(cacheKey, persistent); | ||
| return persistent.resolution; | ||
| } | ||
| const candidates = [ | ||
| this.probeOrca(projectRoot, target, env), | ||
| this.probeNative(target, input.capability, now), | ||
| this.probeTargetCli(projectRoot, target), | ||
| this.buildGenericCandidate(input.requiresIndependentWorker === true), | ||
| ]; | ||
| const matching = preference === 'auto' | ||
| ? candidates | ||
| : candidates.filter(candidate => candidate.kind === preference); | ||
| let selected = matching.find(candidate => candidate.available) || null; | ||
| const candidate = this.buildNativeCandidate(target, input.capability, now); | ||
| const warnings = []; | ||
| if (preference !== 'auto' && !selected) { | ||
| const reason = matching.map(candidate => candidate.reason).join('; ') || `No ${preference} adapter is registered for target ${target}.`; | ||
| if (strict) { | ||
| warnings.push(`Strict adapter preference "${preference}" is unavailable: ${reason}`); | ||
| } | ||
| else { | ||
| warnings.push(`Preferred adapter "${preference}" is unavailable; continuing through the safe fallback chain: ${reason}`); | ||
| selected = candidates.find(candidate => candidate.available) || null; | ||
| } | ||
| const legacyPreference = preference !== 'auto' && preference !== 'native'; | ||
| if (legacyPreference) { | ||
| warnings.push(`Execution adapter preference "${preference}" was removed. OSpec now dispatches only through the current model harness native subagent API.`); | ||
| } | ||
| const selected = candidate.available && !(legacyPreference && strict) | ||
| ? candidate | ||
| : null; | ||
| if (legacyPreference && strict) { | ||
| warnings.push('Strict legacy adapter selection cannot fall back to native execution; change the preference to "native" or "auto".'); | ||
| } | ||
| if (!selected) { | ||
| warnings.push(candidate.reason); | ||
| warnings.push(input.requiresIndependentWorker | ||
| ? 'No independent worker adapter is available; the generic current-controller fallback is intentionally disabled for independent work.' | ||
| : 'No executable adapter is available for this runtime.'); | ||
| ? 'Independent work requires a fresh native subagent in the current model session.' | ||
| : 'Executable work requires a current model session with native subagent support.'); | ||
| } | ||
| const resolution = { | ||
| version: '1.0', | ||
| return { | ||
| version: '2.0', | ||
| preference, | ||
@@ -114,298 +93,72 @@ strict, | ||
| selected, | ||
| fallbackOrder: candidates.map(candidate => candidate.id), | ||
| candidates, | ||
| fallbackOrder: [candidate.id], | ||
| candidates: [candidate], | ||
| blocked: selected === null, | ||
| warnings, | ||
| warnings: unique(warnings), | ||
| }; | ||
| this.resolutionCache.set(cacheKey, { | ||
| expiresAt: now.getTime() + RESOLUTION_CACHE_TTL_MS, | ||
| resolution, | ||
| }); | ||
| this.writePersistentCache(input.cacheFilePath, cacheKey, now, resolution); | ||
| return resolution; | ||
| } | ||
| probeOrca(projectRoot, target, env) { | ||
| const agentCommand = target === 'gpt' ? 'codex' : target; | ||
| if (!['codex', 'gpt', 'claude', 'gemini', 'opencode', 'cursor', 'copilot'].includes(target)) { | ||
| return this.unavailable('orca-terminal', 'orca', `Orca cannot infer an agent command for target ${target}.`); | ||
| } | ||
| const declaredWorktree = extractOrcaWorktreePath(env.ORCA_WORKTREE_ID || env.ORCA_WORKSPACE_ID); | ||
| if (declaredWorktree && !isSameOrAncestor(declaredWorktree, projectRoot, this.platform)) { | ||
| return this.unavailable('orca-terminal', 'orca', `Orca environment identifies a different worktree (${declaredWorktree}).`, false); | ||
| } | ||
| const probes = []; | ||
| const configured = String(env.OSPEC_ORCA_CLI || '').trim(); | ||
| if (configured) | ||
| probes.push(configured); | ||
| if (this.platform === 'win32' && env.LOCALAPPDATA) { | ||
| probes.push(path.win32.join(env.LOCALAPPDATA, 'Programs', 'orca', 'resources', 'bin', 'orca.cmd')); | ||
| } | ||
| if (this.platform === 'linux') | ||
| probes.push('orca-ide', 'orca-dev', 'orca'); | ||
| else | ||
| probes.push('orca', 'orca-dev', 'orca-ide'); | ||
| const failures = []; | ||
| for (const bin of unique(probes)) { | ||
| if (!this.isCommandAvailable(bin, projectRoot)) { | ||
| failures.push(`${bin}: not on PATH`); | ||
| continue; | ||
| } | ||
| const status = this.runCommand(bin, ['status', '--json'], projectRoot); | ||
| if (status.status !== 0 || !parseJson(status.stdout)) { | ||
| failures.push(`${bin}: status probe failed${diagnosticSuffix(status)}`); | ||
| continue; | ||
| } | ||
| const current = this.runCommand(bin, ['worktree', 'current', '--json'], projectRoot); | ||
| const currentJson = current.status === 0 ? parseJson(current.stdout) : null; | ||
| if (!currentJson) { | ||
| failures.push(`${bin}: current worktree probe failed${diagnosticSuffix(current)}`); | ||
| continue; | ||
| } | ||
| const owned = extractPaths(currentJson).some(candidate => isSameOrAncestor(candidate, projectRoot, this.platform)); | ||
| if (!owned) { | ||
| failures.push(`${bin}: current worktree does not own ${projectRoot}`); | ||
| continue; | ||
| } | ||
| return { | ||
| id: 'orca-terminal', | ||
| kind: 'orca', | ||
| available: true, | ||
| verified: true, | ||
| reason: `Verified by ${bin} status and current-worktree probes.`, | ||
| binary: bin, | ||
| workspaceOwned: true, | ||
| supportsParallel: true, | ||
| requiresControllerAction: false, | ||
| commandTemplates: [ | ||
| this.commandTemplate(bin, ['terminal', 'create', '--worktree', 'active', '--title', '<task-title>', '--command', agentCommand, '--json']), | ||
| this.commandTemplate(bin, ['terminal', 'wait', '--terminal', '<terminal-handle>', '--for', 'tui-idle', '--timeout-ms', '60000', '--json']), | ||
| this.commandTemplate(bin, ['terminal', 'send', '--terminal', '<terminal-handle>', '--text', '<adapter-packet-prompt>', '--enter', '--json']), | ||
| ], | ||
| }; | ||
| } | ||
| return this.unavailable('orca-terminal', 'orca', `No verified Orca CLI/worktree pair (${failures.join('; ') || 'no CLI candidates'}).`, failures.some(item => item.includes('does not own')) ? false : null); | ||
| } | ||
| probeNative(target, capability, now) { | ||
| const expiresAt = capability?.expiresAt ? Date.parse(capability.expiresAt) : Number.NaN; | ||
| const current = capability?.controllerAvailable === true | ||
| && capability.nativeSubagentCapability === 'supported' | ||
| && Number.isFinite(expiresAt) | ||
| && expiresAt > now.getTime(); | ||
| buildNativeCandidate(target, capability, now) { | ||
| const definition = NATIVE_TARGETS[target]; | ||
| const reason = this.getAvailabilityReason(target, definition, capability, now); | ||
| const available = reason === null; | ||
| return { | ||
| id: `${target}-harness-native`, | ||
| kind: 'native', | ||
| available: current, | ||
| verified: current, | ||
| reason: current | ||
| ? `Current session-bound harness capability authorizes the ${target} native adapter.` | ||
| : `No current session-bound native-subagent capability authorizes target ${target}.`, | ||
| binary: null, | ||
| workspaceOwned: null, | ||
| supportsParallel: current, | ||
| requiresControllerAction: true, | ||
| commandTemplates: [], | ||
| }; | ||
| } | ||
| probeTargetCli(projectRoot, target) { | ||
| const binary = target === 'gpt' ? 'codex' : target; | ||
| const supported = ['codex', 'gpt', 'claude', 'gemini', 'opencode'].includes(target); | ||
| if (!supported) { | ||
| return this.unavailable(`${target}-cli`, 'cli', `No safe direct CLI contract is registered for target ${target}.`); | ||
| } | ||
| const available = this.isCommandAvailable(binary, projectRoot); | ||
| const args = target === 'claude' | ||
| ? ['-p', '<adapter-packet-prompt>'] | ||
| : target === 'codex' || target === 'gpt' | ||
| ? ['exec', '<adapter-packet-prompt>'] | ||
| : target === 'gemini' | ||
| ? ['-p', '<adapter-packet-prompt>'] | ||
| : ['run', '<adapter-packet-prompt>']; | ||
| return { | ||
| id: `${binary}-cli`, | ||
| kind: 'cli', | ||
| available, | ||
| verified: available, | ||
| reason: available ? `${binary} is callable without a shell.` : `${binary} is not on PATH.`, | ||
| binary, | ||
| reason: available | ||
| ? `Current session-bound ${target} capability authorizes its native subagent adapter.` | ||
| : reason || `Native subagent adapter for ${target} is unavailable.`, | ||
| binary: null, | ||
| workspaceOwned: null, | ||
| supportsParallel: available, | ||
| requiresControllerAction: false, | ||
| commandTemplates: available ? [{ bin: binary, args }] : [], | ||
| }; | ||
| } | ||
| buildGenericCandidate(independent) { | ||
| return { | ||
| id: 'generic-current-controller', | ||
| kind: 'generic', | ||
| available: !independent, | ||
| verified: true, | ||
| reason: independent | ||
| ? 'Independent work cannot fall back to the same controller context.' | ||
| : 'Execute serially in the current controller context without claiming a child process.', | ||
| binary: null, | ||
| workspaceOwned: null, | ||
| supportsParallel: false, | ||
| requiresControllerAction: true, | ||
| commandTemplates: [], | ||
| nativeSubagent: definition | ||
| ? { target, ...definition } | ||
| : null, | ||
| }; | ||
| } | ||
| unavailable(id, kind, reason, workspaceOwned = null) { | ||
| return { | ||
| id, | ||
| kind, | ||
| available: false, | ||
| verified: false, | ||
| reason, | ||
| binary: null, | ||
| workspaceOwned, | ||
| supportsParallel: false, | ||
| requiresControllerAction: false, | ||
| commandTemplates: [], | ||
| }; | ||
| } | ||
| isCommandAvailable(bin, cwd) { | ||
| if (this.pathApi().isAbsolute(bin)) | ||
| return this.pathExists(bin); | ||
| const result = this.platform === 'win32' | ||
| ? this.commandRunner('where', [bin], { cwd, timeoutMs: COMMAND_TIMEOUT_MS }) | ||
| : this.commandRunner('sh', ['-lc', 'command -v "$1"', 'ospec-probe', bin], { cwd, timeoutMs: COMMAND_TIMEOUT_MS }); | ||
| return result.status === 0 && result.stdout.trim().length > 0; | ||
| } | ||
| runCommand(bin, args, cwd) { | ||
| const invocation = this.resolveInvocation(bin, args); | ||
| return this.commandRunner(invocation.bin, invocation.args, { | ||
| cwd, | ||
| timeoutMs: COMMAND_TIMEOUT_MS, | ||
| environment: invocation.environment, | ||
| }); | ||
| } | ||
| commandTemplate(bin, args) { | ||
| return this.resolveInvocation(bin, args); | ||
| } | ||
| resolveInvocation(bin, args) { | ||
| const pathApi = this.pathApi(); | ||
| if (this.platform === 'win32' && pathApi.basename(bin).toLowerCase() === 'orca.cmd') { | ||
| const resourcesDir = pathApi.resolve(pathApi.dirname(bin), '..'); | ||
| const electron = pathApi.resolve(resourcesDir, '..', 'Orca.exe'); | ||
| const cli = pathApi.join(resourcesDir, 'app.asar.unpacked', 'out', 'cli', 'index.js'); | ||
| if (this.pathExists(electron) && this.pathExists(cli)) { | ||
| return { | ||
| bin: electron, | ||
| args: [cli, ...args], | ||
| environment: { | ||
| ELECTRON_RUN_AS_NODE: '1', | ||
| NODE_OPTIONS: '', | ||
| NODE_REPL_EXTERNAL_MODULE: '', | ||
| }, | ||
| }; | ||
| } | ||
| getAvailabilityReason(target, definition, capability, now) { | ||
| if (!definition) { | ||
| return `Target "${target}" has no registered model-native subagent primitive.`; | ||
| } | ||
| return { bin, args }; | ||
| } | ||
| pathApi() { | ||
| return this.platform === 'win32' ? path.win32 : path.posix; | ||
| } | ||
| cacheKey(input) { | ||
| return JSON.stringify({ | ||
| projectRoot: input.projectRoot, | ||
| target: input.target, | ||
| preference: input.preference, | ||
| strict: input.strict, | ||
| independent: input.independent, | ||
| capability: input.capability | ||
| ? { | ||
| source: input.capability.probeSource, | ||
| native: input.capability.nativeSubagentCapability, | ||
| controller: input.capability.controllerAvailable, | ||
| expiresAt: input.capability.controllerAvailable | ||
| && input.capability.nativeSubagentCapability === 'supported' | ||
| ? input.capability.expiresAt | ||
| : null, | ||
| } | ||
| : null, | ||
| orca: { | ||
| cli: input.env.OSPEC_ORCA_CLI || null, | ||
| localAppData: input.env.LOCALAPPDATA || null, | ||
| worktree: input.env.ORCA_WORKTREE_ID || input.env.ORCA_WORKSPACE_ID || null, | ||
| terminal: input.env.ORCA_TERMINAL_HANDLE || null, | ||
| }, | ||
| }); | ||
| } | ||
| readPersistentCache(cacheFilePath, cacheKey, now) { | ||
| if (!cacheFilePath) | ||
| return null; | ||
| try { | ||
| const raw = JSON.parse((0, fs_1.readFileSync)(path.resolve(cacheFilePath), 'utf8')); | ||
| const entry = raw.version === PERSISTENT_CACHE_VERSION | ||
| ? raw.entries?.[cacheKey] | ||
| : raw.version === '1.0' && raw.cacheKey === cacheKey | ||
| ? raw | ||
| : undefined; | ||
| const expiresAt = Date.parse(String(entry?.expiresAt || '')); | ||
| if (!entry | ||
| || !Number.isFinite(expiresAt) | ||
| || expiresAt <= now.getTime() | ||
| || !isValidCachedResolution(entry.resolution)) | ||
| return null; | ||
| return { expiresAt, resolution: entry.resolution }; | ||
| if (!capability) { | ||
| return `No session-bound native-subagent capability was reported for target ${target}.`; | ||
| } | ||
| catch { | ||
| return null; | ||
| if (normalizeTarget(capability.target) !== target) { | ||
| return `Capability target "${capability.target || 'unbound'}" does not match requested target "${target}".`; | ||
| } | ||
| } | ||
| writePersistentCache(cacheFilePath, cacheKey, now, resolution) { | ||
| if (!cacheFilePath) | ||
| return; | ||
| const resolved = path.resolve(cacheFilePath); | ||
| try { | ||
| (0, fs_1.mkdirSync)(path.dirname(resolved), { recursive: true }); | ||
| let entries = {}; | ||
| try { | ||
| const existing = JSON.parse((0, fs_1.readFileSync)(resolved, 'utf8')); | ||
| if (existing.version === PERSISTENT_CACHE_VERSION && existing.entries) { | ||
| entries = Object.fromEntries(Object.entries(existing.entries).filter(([, entry]) => { | ||
| const expiresAt = Date.parse(String(entry?.expiresAt || '')); | ||
| return Number.isFinite(expiresAt) && expiresAt > now.getTime() && isValidCachedResolution(entry?.resolution); | ||
| })); | ||
| } | ||
| } | ||
| catch { | ||
| // Missing, legacy, or partially written caches are replaced below. | ||
| } | ||
| entries[cacheKey] = { | ||
| generatedAt: now.toISOString(), | ||
| expiresAt: new Date(now.getTime() + RESOLUTION_CACHE_TTL_MS).toISOString(), | ||
| resolution, | ||
| }; | ||
| (0, fs_1.writeFileSync)(resolved, `${JSON.stringify({ | ||
| version: PERSISTENT_CACHE_VERSION, | ||
| entries, | ||
| }, null, 2)}\n`, { encoding: 'utf8' }); | ||
| if (capability.interactive !== true | ||
| || capability.controllerAvailable !== true | ||
| || capability.nativeSubagentCapability !== 'supported') { | ||
| return `Native subagent capability for target ${target} is not currently authorized.`; | ||
| } | ||
| catch { | ||
| // Cache writes are an optimization and must never block execution. | ||
| const reportedAt = Date.parse(capability.reportedAt); | ||
| const expiresAt = Date.parse(capability.expiresAt); | ||
| if (!Number.isFinite(reportedAt) || !Number.isFinite(expiresAt)) { | ||
| return `Native subagent capability for target ${target} has an invalid session lifetime.`; | ||
| } | ||
| if (expiresAt <= reportedAt) { | ||
| return `Native subagent capability for target ${target} has a non-positive session lifetime.`; | ||
| } | ||
| if (reportedAt > now.getTime()) { | ||
| return `Native subagent capability for target ${target} is future-dated and cannot authorize this session.`; | ||
| } | ||
| if (expiresAt <= now.getTime()) { | ||
| return `Native subagent capability for target ${target} expired at ${capability.expiresAt}.`; | ||
| } | ||
| return null; | ||
| } | ||
| } | ||
| exports.RuntimeExecutionAdapterService = RuntimeExecutionAdapterService; | ||
| function defaultCommandRunner(bin, args, options) { | ||
| const result = (0, child_process_1.spawnSync)(bin, args, { | ||
| cwd: options.cwd, | ||
| env: options.environment ? { ...process.env, ...options.environment } : process.env, | ||
| encoding: 'utf8', | ||
| timeout: options.timeoutMs, | ||
| windowsHide: true, | ||
| shell: false, | ||
| maxBuffer: 1024 * 1024, | ||
| }); | ||
| return { | ||
| status: typeof result.status === 'number' ? result.status : null, | ||
| stdout: String(result.stdout || ''), | ||
| stderr: String(result.stderr || ''), | ||
| error: result.error?.message, | ||
| }; | ||
| function createRuntimeExecutionAdapterService() { | ||
| return new RuntimeExecutionAdapterService(); | ||
| } | ||
| function normalizeTarget(value) { | ||
| return String(value || 'generic').trim().toLowerCase() || 'generic'; | ||
| } | ||
| function normalizePreference(value) { | ||
| const normalized = String(value || 'auto').trim().toLowerCase(); | ||
| return normalized === 'orca' || normalized === 'native' || normalized === 'cli' || normalized === 'generic' | ||
| return normalized === 'native' || normalized === 'orca' || normalized === 'cli' || normalized === 'generic' | ||
| ? normalized | ||
@@ -415,61 +168,11 @@ : 'auto'; | ||
| function normalizeBoolean(value) { | ||
| const normalized = String(value ?? '').trim().toLowerCase(); | ||
| if (normalized === '1' || normalized === 'true' || normalized === 'yes') | ||
| const normalized = String(value || '').trim().toLowerCase(); | ||
| if (['1', 'true', 'yes'].includes(normalized)) | ||
| return true; | ||
| if (normalized === '0' || normalized === 'false' || normalized === 'no') | ||
| if (['0', 'false', 'no'].includes(normalized)) | ||
| return false; | ||
| return null; | ||
| } | ||
| function parseJson(value) { | ||
| try { | ||
| const parsed = JSON.parse(String(value || '').trim()); | ||
| return parsed && typeof parsed === 'object' ? parsed : null; | ||
| } | ||
| catch { | ||
| return null; | ||
| } | ||
| } | ||
| function extractPaths(value, key = '') { | ||
| if (typeof value === 'string') { | ||
| return /(?:^|_)(?:path|root|cwd)$/i.test(key) || /(?:Path|Root|Cwd)$/.test(key) ? [value] : []; | ||
| } | ||
| if (Array.isArray(value)) | ||
| return value.flatMap(item => extractPaths(item)); | ||
| if (!value || typeof value !== 'object') | ||
| return []; | ||
| return Object.entries(value).flatMap(([childKey, child]) => extractPaths(child, childKey)); | ||
| } | ||
| function isSameOrAncestor(candidate, projectRoot, platform) { | ||
| const pathApi = platform === 'win32' ? path.win32 : path.posix; | ||
| const resolvedCandidate = pathApi.resolve(candidate); | ||
| const relative = pathApi.relative(resolvedCandidate, projectRoot); | ||
| return relative === '' || (!relative.startsWith('..') && !pathApi.isAbsolute(relative)); | ||
| } | ||
| function diagnosticSuffix(result) { | ||
| const detail = result.error || result.stderr.trim(); | ||
| return detail ? ` (${detail.slice(0, 180)})` : ''; | ||
| } | ||
| function extractOrcaWorktreePath(value) { | ||
| const normalized = String(value || '').trim(); | ||
| const separator = normalized.indexOf('::'); | ||
| if (separator < 0 || separator + 2 >= normalized.length) | ||
| return null; | ||
| return normalized.slice(separator + 2).trim() || null; | ||
| } | ||
| function unique(values) { | ||
| return [...new Set(values.filter(Boolean))]; | ||
| } | ||
| function isValidCachedResolution(value) { | ||
| if (!value || typeof value !== 'object') | ||
| return false; | ||
| const candidate = value; | ||
| return candidate.version === '1.0' | ||
| && typeof candidate.target === 'string' | ||
| && typeof candidate.blocked === 'boolean' | ||
| && Array.isArray(candidate.fallbackOrder) | ||
| && Array.isArray(candidate.candidates) | ||
| && (candidate.selectedAdapterId === null || typeof candidate.selectedAdapterId === 'string'); | ||
| } | ||
| function createRuntimeExecutionAdapterService() { | ||
| return new RuntimeExecutionAdapterService(); | ||
| } |
@@ -300,2 +300,3 @@ import { AgentModelProfileId, DocumentReviewPolicy } from '../core/types'; | ||
| reviewerHeartbeatAt?: string | null; | ||
| reviewerLeaseExpiresAt?: string | null; | ||
| reviewerCompletedAt?: string | null; | ||
@@ -1062,4 +1063,4 @@ reviewerSucceeded?: boolean | null; | ||
| * Loop/agent-primitive plan attached to a launch when `--primitive goal|loop` is requested. | ||
| * Produces instructions (controller-driven) or an external CLI preview (cli-driven); ospec never | ||
| * executes the agent itself (Execution-Model Contract 1). Absent for the default `subagent` primitive. | ||
| * Produces controller instructions for the current model harness; OSpec never executes the agent. | ||
| * Absent for the default `subagent` primitive. | ||
| */ | ||
@@ -1299,5 +1300,4 @@ export interface TaskLaunchLoopPlan { | ||
| /** | ||
| * Build the loop/agent-primitive plan for a goal|loop launch (Execution-Model Contracts 1–3). | ||
| * Controller-driven => produce instructions; cli-driven => provide a `claude -p`/`codex exec` | ||
| * dry-run preview. Never executes the agent and never emits `claude --goal`. | ||
| * Build the loop/agent-primitive plan for a goal|loop launch. OSpec only | ||
| * produces controller instructions; the model harness owns native dispatch. | ||
| */ | ||
@@ -1409,2 +1409,5 @@ private buildLaunchLoopPlan; | ||
| completeDocumentReviewExecutor(changePath: string, stage: TaskDocumentReviewStage, executorId: string): Promise<TaskDocumentReviewDispatchRecord>; | ||
| private documentReviewExecutorLeaseExpiresAt; | ||
| private isDocumentReviewExecutorLeaseExpired; | ||
| private releaseDocumentReviewExecutorClaimUnlocked; | ||
| private readCurrentDocumentReviewDispatch; | ||
@@ -1605,2 +1608,3 @@ private updateDocumentReviewExecutorProvenance; | ||
| private readTaskGraphLockOwner; | ||
| private refreshTaskGraphLockIfOwned; | ||
| private isProcessAlive; | ||
@@ -1690,3 +1694,2 @@ private removeTaskGraphLockIfOwned; | ||
| private getNativeAgentDispatchMode; | ||
| private buildWorkerLaunchCommands; | ||
| private buildWorkerLaunchPrompt; | ||
@@ -1693,0 +1696,0 @@ private runWorkerCommand; |
@@ -144,4 +144,2 @@ "use strict"; | ||
| ospec execute launch [change-path|project-path] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--dry-run] [--json] - write the capability-based runtime adapter launch plan without starting workers; --json prints the machine-readable artifact | ||
| ospec execute orchestrate [change-path|project-path] --command "..." [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] [--limit N] [--max-rounds N] [--timeout-ms N] - execute the adapter-selected explicit CLI orchestration path, with failed-worker retry guidance | ||
| ospec execute launch [change-path|project-path] [--task task-id] [--target codex|gpt|claude|gemini|opencode|cursor|copilot|shell|generic] --run --command "..." [--timeout-ms N] - fallback only: run one explicit local worker command and capture stdout/stderr under artifacts/agents/worker-runs/ | ||
| ospec execute collect [change-path|project-path] [--task task-id] [--run run-id] [--status DONE|DONE_WITH_CONCERNS|NEEDS_CONTEXT|BLOCKED] [--summary "..."] - collect a worker run into task completion state | ||
@@ -152,3 +150,2 @@ ospec execute retry [change-path|project-path] --task task-id [--run run-id] [--summary "..."] [--force] - reopen a blocked or failed task and create a fresh dispatch packet | ||
| ospec execute review [change-path|project-path] [--task task-id] [--stage spec|quality] - create the next safe task-level or final review dispatch packet with project session context | ||
| ospec execute review [change-path|project-path] [--task task-id] [--stage spec|quality] --run --command "..." [--timeout-ms N] [--usage-file usage.json] [--decision APPROVED|APPROVED_WITH_CONCERNS|NEEDS_CHANGES|BLOCKED|PENDING] [--summary "..."] - run an explicit local reviewer command, automatically expose OSPEC_USAGE_FILE, and optionally record the decision | ||
| ospec execute feedback [change-path|project-path] [--stage spec|quality] [--summary "..."] - write a review feedback handling plan without editing source files | ||
@@ -161,3 +158,3 @@ ospec execute repair [change-path|project-path] - convert all NEEDS_CHANGES final-review findings into one grouped repair task and dispatch | ||
| ospec execute require-verification [change-path|project-path] --id id --kind browser|e2e|test|lint|build|manual|other --description "..." [--required|--optional] - persist a verification surface that final verification and archive must enforce | ||
| ospec execute verify [change-path|project-path] --command "npm test" [--status PASSED|FAILED|BLOCKED|SKIPPED] [--satisfies requirement-id] [--exit-code 0] [--summary "..."] - record verification evidence and bind it to required verification surfaces | ||
| ospec execute verify [change-path|project-path] --command "npm test" [--status PASSED|FAILED|BLOCKED|SKIPPED] [--satisfies requirement-id] [--exit-code N] [--summary "..."] - record verification evidence; PASSED requires --exit-code 0 | ||
| ospec execute help - show execute command help | ||
@@ -164,0 +161,0 @@ `; |
+1
-1
| { | ||
| "name": "@clawplays/ospec-cli", | ||
| "version": "1.8.1", | ||
| "version": "1.8.2", | ||
| "description": "Official OSpec CLI package for spec-driven development (SDD) and document-driven development in AI coding agent and CLI workflows.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
+3
-3
@@ -148,3 +148,3 @@ <h1><a href="https://ospec.ai/" target="_blank" rel="noopener noreferrer">OSpec.ai</a></h1> | ||
| `launch` writes `artifacts/agents/launch-plan.md`; it does not start workers by itself. Codex/GPT use `spawn_agent` plus the harness wait lifecycle (and close only when exposed), Claude Code uses Task, Gemini uses `@generalist`, and OpenCode uses `@mention`. Use `launch --run --command` or `orchestrate --command` only when the current harness cannot start native subagents and the user explicitly accepts CLI fallback. | ||
| `launch` writes `artifacts/agents/launch-plan.md`; it does not start workers by itself. Codex/GPT use `spawn_agent` plus `wait_agent`, Claude Code uses Task, Gemini uses `@generalist`, and OpenCode uses `@mention`. OSpec never starts Orca, Codex, Claude, or another agent CLI as a fallback. If the current model harness cannot provide native subagents, executable dispatch blocks until a supported harness reports a fresh capability. | ||
@@ -325,4 +325,4 @@ ### 3. Archive After Acceptance | ||
| - **Session brief and hooks**: `ospec session` writes `.ospec/session-brief.json` and `.ospec/session-brief.md` so agents or humans entering an existing project can see active changes, queued changes, queue-run state, indexed document and archived-feature counts, a cache fingerprint, and the next safe command before touching a change; `ospec session hook --target claude` writes opt-in harness startup artifacts plus a Claude Code hook bundle under `.ospec/hooks/`, and `--apply` idempotently merges it into `.claude/settings.json`. | ||
| - **Integrated goal loop**: `ospec loop run --once` emits token-bounded task/review/verification action batches for fresh native subagents, while CLI-driven `ospec loop watch` executes supported external agents in fresh processes. Both use packet paths instead of duplicating the whole goal, persist pending actions and feedback, route review failures through retry or one grouped repair wave, and enforce required decisions, L3 allowlists, budgets, no-progress stops, and comprehension-review pauses. | ||
| - **Task graph controller**: `ospec execute bootstrap` writes a one-change startup/resume snapshot with the project session brief snapshot and next safe action; `handoff` writes a cross-tool worker handoff guide with the project session brief snapshot; `doc-review` creates design and implementation-plan reviewer packets before task execution; `status` and `next` report controller state and safe next task candidates; `workspace` records git workspace safety before worker handoff; `worktree` records an isolated-worktree preparation plan by default, while explicit `--create` or `--cleanup` runs the matching git worktree command and captures `artifacts/agents/worktree-runs/`; `finish` records closeout readiness before finalize, archive, push, merge, or worktree cleanup; `dispatch` and `complete` create parallel-safe worker packets with worker profiles and target tool mapping, then record task results as OSpec artifacts; `review --task` creates one combined per-task code review packet (spec compliance + code quality in a single pass) that blocks dependent tasks until approved, while final `review` creates one combined whole-change code review packet; `launch` writes the native agent launch plan for the current AI harness, including Codex/GPT `spawn_agent`, Claude Code Task, Gemini `@generalist`, and OpenCode `@mention` guidance; `orchestrate` is the final CLI fallback for harnesses without native subagents and runs explicit command templates only; `launch --run --command` is the single-worker CLI fallback; `collect` turns a fallback worker run into task completion state; `retry` reopens blocked, needs-context, or failed task work; explicit review `--run --command` captures `artifacts/agents/review-runs/`; `debug`, `tdd`, and `verify` record durable evidence; `sync` rebuilds `worker-status.md` from execution and review artifacts. | ||
| - **Integrated goal loop**: `ospec loop run --once` emits token-bounded task/review/verification action batches for fresh model-native subagents. It uses packet paths instead of duplicating the whole goal, persists pending actions and feedback, routes review failures through retry or one grouped repair wave, and enforces required decisions, L3 allowlists, budgets, no-progress stops, and comprehension-review pauses. The removed `loop watch` path now returns migration guidance without starting an agent process. | ||
| - **Task graph controller**: `ospec execute bootstrap` writes a one-change startup/resume snapshot with the project session brief snapshot and next safe action; `handoff` writes a cross-tool worker handoff guide; `doc-review` creates design and implementation-plan reviewer packets; `status` and `next` report controller state; `workspace` records git workspace safety; `worktree` manages explicit git worktree plans/runs; `dispatch`, `launch`, `complete`, and `review` create and settle native-subagent packets; `retry` reopens blocked or needs-context work; `debug`, `tdd`, and `verify` record durable evidence; `sync` rebuilds `worker-status.md`. `orchestrate`, `launch --run --command`, and `review --run --command` are retained only as migration errors and never launch agent CLIs. | ||
| - **Adaptive review and model routing**: `.skillrc.workflow.document_review_policy` keeps independent document review by default. With `adaptive`, inline preflight still requires an explicit `risk_level: low` (or `none`) declaration and no detected risk signal; `.skillrc.workflow.model_profiles` maps logical roles without provider model names in OSpec defaults. | ||
@@ -329,0 +329,0 @@ - **Measured execution and grouped repair**: command runners can write authoritative usage to `OSPEC_USAGE_FILE` for automatic ingestion, while `--usage-file` remains a manual input. Metrics distinguish complete, partial, and missing coverage. `ospec execute repair` turns all structured `NEEDS_CHANGES` findings into one repair task. |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
67
-10.67%6
-25%2939118
-1.16%52450
-0.64%