@clawplays/ospec-cli
Advanced tools
| # بروتوكول Classic Change | ||
| استخدم هذا البروتوكول المختصر عندما يختار المستخدم OSpec change. اختيار profile للمستخدم؛ لا ترقِّ change تلقائياً إلى Goal ولا ترفضها أو تستبدلها بسبب التعقيد أو flags أو عدد الملفات أو حجم الدفعة. | ||
| ## السياق | ||
| في البداية اقرأ `.skillrc` والعناصر ذات الصلة من `SKILL.index.json` و`proposal.md` و`tasks.md` و`state.json` فقط. اقرأ `verification.md` عند التحقق و`review.md` عند closeout. اقرأ `ai-guide.md` أو `execution-protocol.md` الكامل فقط عند غياب هذا الملف أو تفعيل plugin حاجب أو وجود قاعدة محددة غامضة. | ||
| ## دورة الحياة | ||
| 1. أنشئ العمل الجديد عبر `ospec change <change-name> [path]`، ويبقى `ospec new` alias للتوافق. | ||
| 2. إذا وُجد active change مطابق فتابعه ولا تنشئ نسخة مكررة. | ||
| 3. تدخل تغييرات batch إلى queue وتُنفذ بالتتابع في worktree المشترك. | ||
| 4. حافظ فقط على `proposal.md` و`tasks.md` و`state.json` و`verification.md` و`review.md`، ولا تنشئ design أو plan أو task graph أو worker أو review provenance artifacts الخاصة بـ Goal. | ||
| 5. شغّل فحوص المشروع المرتبطة فعلاً بالتغيير وسجّل الأوامر والنتائج في `verification.md`، ولا تفرض build أو lint أو test أو TDD أو debug غير ذي صلة. | ||
| 6. ينفذ AI الحالي مراجعة خفيفة واحدة. يمكن إغلاق `APPROVED` و`APPROVED_WITH_CONCERNS` تلقائياً، بينما توقف `PENDING` و`NEEDS_CHANGES` و`BLOCKED` الإغلاق. | ||
| 7. شغّل `ospec verify` عند الحاجة إلى preview صريح. بعد اكتمال التنفيذ والتحقق وسياسة الوثائق وplugin gates والمراجعة، شغّل `ospec finalize` فوراً لمزامنة classic state والأرشفة بشكل ذري. | ||
| ## سياسة الوثائق | ||
| اضبط `change_type` على `bugfix` أو `feature` أو `maintenance` أو `docs`، واضبط `documentation_impact` على `none` أو `required`. | ||
| - يمكن للـ bugfix استخدام `none` مع `documentation_reason` واضح، إلا إذا غيّر سلوك المستخدم أو API أو عقد التشغيل. | ||
| - يجب أن تستخدم feature أو docs change القيمة `required` وأن تسجل وثيقة مشروع أو module أو API أو user حقيقية واحدة على الأقل في `documentation_updates`. | ||
| - لا يُحتسب ملخص `docs/project/changes/...` المولد تلقائياً كوثائق feature. | ||
| - حدّث `SKILL.md` فقط عند تغير قواعد module أو تعليمات AI أو عقود الاستخدام. | ||
| - يعاد بناء `SKILL.index.json` تلقائياً بعد archive وليس task يدوية. | ||
| توقف فقط لقرار مستخدم حقيقي أو فشل تحقق أو review غير محلولة أو plugin gate حاجب أو pause صريح. |
| # Classic Change Protocol | ||
| Use this compact protocol when the user selected an OSpec change. Profile selection belongs to the user: never promote, reject, or replace a change with a Goal because of complexity, flags, file count, or batch size. | ||
| ## Context | ||
| At start, read `.skillrc`, the relevant `SKILL.index.json` entries, `proposal.md`, `tasks.md`, and `state.json`. Read `verification.md` when entering verification and `review.md` during closeout. Read the full `ai-guide.md` or `execution-protocol.md` only when this compact file is missing, a blocking plugin is active, or a specific ambiguous rule requires it. | ||
| ## Lifecycle | ||
| 1. Create new work with `ospec change <change-name> [path]`; `ospec new` remains a compatibility alias. | ||
| 2. Continue an existing matching active change instead of duplicating it. | ||
| 3. Batch changes go through the queue and run sequentially in the shared worktree. | ||
| 4. Keep only `proposal.md`, `tasks.md`, `state.json`, `verification.md`, and `review.md` aligned. Do not create Goal design, plan, task graph, worker, or review-provenance artifacts. | ||
| 5. Run project checks relevant to the actual change and record commands and results in `verification.md`; do not require unrelated build, lint, test, TDD, or debug commands. | ||
| 6. The current AI performs one lightweight review. `APPROVED` and `APPROVED_WITH_CONCERNS` may close automatically; `PENDING`, `NEEDS_CHANGES`, and `BLOCKED` stop closeout. | ||
| 7. Run `ospec verify` when an explicit preview is useful. Once implementation, verification, documentation policy, plugin gates, and review are ready, run `ospec finalize` immediately. Finalize synchronizes classic state and archives atomically. | ||
| ## Documentation | ||
| Set `change_type` to `bugfix`, `feature`, `maintenance`, or `docs`. Set `documentation_impact` to `none` or `required`. | ||
| - A bugfix may use `none` with a concrete `documentation_reason`, unless it changes user behavior, an API, or an operating contract. | ||
| - A feature or docs change must use `required` and list at least one real project, module, API, or user document in `documentation_updates`. | ||
| - The generated `docs/project/changes/...` archive summary never satisfies feature documentation. | ||
| - Update `SKILL.md` only when module rules, AI instructions, or usage contracts changed. | ||
| - `SKILL.index.json` is rebuilt automatically after archive and is not a manual task. | ||
| Stop only for a real user decision, failed verification, unresolved review, blocking plugin gate, or explicit pause. |
| # Classic Change プロトコル | ||
| ユーザーが OSpec change を選んだ場合は、この簡潔なプロトコルを使います。profile の選択はユーザーに属し、複雑さ、flags、ファイル数、batch 数を理由に change を Goal へ自動昇格、拒否、置換してはいけません。 | ||
| ## コンテキスト | ||
| 開始時は `.skillrc`、`SKILL.index.json` の関連項目、`proposal.md`、`tasks.md`、`state.json` だけを読みます。検証時に `verification.md`、closeout 時に `review.md` を読みます。本ファイルがない、blocking plugin が有効、または特定ルールが曖昧な場合だけ完全な `ai-guide.md` や `execution-protocol.md` を読みます。 | ||
| ## ライフサイクル | ||
| 1. 新規作業は `ospec change <change-name> [path]` で作成し、`ospec new` は互換 alias として残します。 | ||
| 2. 一致する active change が既にあれば重複作成せず継続します。 | ||
| 3. batch change は queue に入れ、共有 worktree で順番に実行します。 | ||
| 4. `proposal.md`、`tasks.md`、`state.json`、`verification.md`、`review.md` だけを同期し、Goal の design、plan、task graph、worker、review provenance artifacts は作りません。 | ||
| 5. 実際の change に関連する project check だけを実行して command と結果を `verification.md` に記録します。無関係な build、lint、test、TDD、debug は要求しません。 | ||
| 6. 現在の AI が軽量 review を 1 回行います。`APPROVED` と `APPROVED_WITH_CONCERNS` は自動 closeout 可能で、`PENDING`、`NEEDS_CHANGES`、`BLOCKED` は停止します。 | ||
| 7. 明示的な preview が必要なら `ospec verify` を実行します。実装、検証、文書方針、plugin gate、review が完了したら直ちに `ospec finalize` を実行し、classic state を同期して atomic に archive します。 | ||
| ## 文書方針 | ||
| `change_type` は `bugfix`、`feature`、`maintenance`、`docs`、`documentation_impact` は `none` または `required` にします。 | ||
| - bugfix は具体的な `documentation_reason` があれば `none` を使えます。ただしユーザー動作、API、運用契約を変える場合は文書更新が必要です。 | ||
| - feature または docs change は `required` とし、`documentation_updates` に実際の project、module、API、user document を 1 つ以上記録します。 | ||
| - 自動生成される `docs/project/changes/...` archive summary は feature 文書に数えません。 | ||
| - module rule、AI instruction、usage contract が変わる場合だけ `SKILL.md` を更新します。 | ||
| - `SKILL.index.json` は archive 後に自動再構築され、手動 task ではありません。 | ||
| 実際のユーザー判断、検証失敗、未解決 review、blocking plugin gate、明示的 pause の場合だけ停止します。 |
| # 经典 Change 协议 | ||
| 用户选择 OSpec change 时使用这个精简协议。profile 选择权属于用户:不得因为复杂度、flags、文件数量或批量任务而把 change 自动升级、拒绝或替换成 Goal。 | ||
| ## 上下文 | ||
| 开始时只读 `.skillrc`、`SKILL.index.json` 的相关条目、`proposal.md`、`tasks.md` 和 `state.json`。进入验证时再读 `verification.md`,收口时再读 `review.md`。只有缺少本文件、启用了阻塞插件或某条规则确实有歧义时,才读取完整 `ai-guide.md` 或 `execution-protocol.md`。 | ||
| ## 生命周期 | ||
| 1. 新工作使用 `ospec change <change-name> [path]`;`ospec new` 保留为兼容别名。 | ||
| 2. 已有匹配的 active change 时继续它,不要重复创建。 | ||
| 3. 批量 change 进入 queue,在共享工作区依次执行。 | ||
| 4. 只维护 `proposal.md`、`tasks.md`、`state.json`、`verification.md` 和 `review.md`;不要创建 Goal 的设计、计划、task graph、worker 或 review provenance artifacts。 | ||
| 5. 只运行与实际改动有关的项目检查,并把命令和结果记录到 `verification.md`;不得强制执行无关的 build、lint、test、TDD 或 debug 命令。 | ||
| 6. 当前 AI 完成一次轻量 review。`APPROVED` 和 `APPROVED_WITH_CONCERNS` 可以自动收口;`PENDING`、`NEEDS_CHANGES` 和 `BLOCKED` 必须停止。 | ||
| 7. 需要显式预览时运行 `ospec verify`。实现、验证、文档策略、插件门禁和 review 都满足后,立即运行 `ospec finalize`;finalize 自动同步 classic state 并原子归档。 | ||
| ## 文档策略 | ||
| 把 `change_type` 设置为 `bugfix`、`feature`、`maintenance` 或 `docs`,把 `documentation_impact` 设置为 `none` 或 `required`。 | ||
| - bugfix 可以用 `none`,但必须写具体 `documentation_reason`;如果改变用户行为、API 或运行契约,仍需更新文档。 | ||
| - feature 或 docs change 必须使用 `required`,并在 `documentation_updates` 中列出至少一个真实项目、模块、API 或用户文档。 | ||
| - 自动生成的 `docs/project/changes/...` 归档摘要不算 feature 文档。 | ||
| - 只有模块规则、AI 指令或使用契约改变时才更新 `SKILL.md`。 | ||
| - `SKILL.index.json` 在归档后自动重建,不是手工 task。 | ||
| 只有真实用户决策、验证失败、review 未解决、阻塞插件门禁或用户明确暂停时才停止。 |
| import { ChangeStatusCheck, FeatureState } from '../core/types'; | ||
| import { PluginWorkflowComposer } from '../workflow/PluginWorkflowComposer'; | ||
| import { FileService } from './FileService'; | ||
| export interface ClassicChangeDocumentationAnalysis { | ||
| changeType: string; | ||
| impact: string; | ||
| updates: string[]; | ||
| archiveReady: boolean; | ||
| checks: ChangeStatusCheck[]; | ||
| } | ||
| export interface ClassicChangeReviewAnalysis { | ||
| decision: string; | ||
| checklistComplete: boolean; | ||
| archiveReady: boolean; | ||
| checks: ChangeStatusCheck[]; | ||
| } | ||
| export interface ClassicChangePluginAnalysis { | ||
| archiveReady: boolean; | ||
| checks: ChangeStatusCheck[]; | ||
| } | ||
| export declare class ClassicChangeCloseoutService { | ||
| private readonly fileService; | ||
| constructor(fileService: FileService); | ||
| analyzeDocumentationContract(projectRoot: string, proposalPath: string): Promise<ClassicChangeDocumentationAnalysis>; | ||
| analyzeReview(reviewPath: string): Promise<ClassicChangeReviewAnalysis>; | ||
| analyzePluginGates(changePath: string, activatedSteps: string[], workflow: PluginWorkflowComposer): Promise<ClassicChangePluginAnalysis>; | ||
| deriveCloseoutState(state: FeatureState, input: { | ||
| proposalReady: boolean; | ||
| tasksReady: boolean; | ||
| verificationReady: boolean; | ||
| reviewReady: boolean; | ||
| documentationReady: boolean; | ||
| pluginsReady: boolean; | ||
| }): FeatureState; | ||
| private validateDocumentationPath; | ||
| } |
| "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.ClassicChangeCloseoutService = void 0; | ||
| const path = __importStar(require("path")); | ||
| const fs_1 = require("fs"); | ||
| const helpers_1 = require("../utils/helpers"); | ||
| const CHANGE_TYPES = new Set(['bugfix', 'feature', 'maintenance', 'docs']); | ||
| const DOCUMENTATION_IMPACTS = new Set(['none', 'required']); | ||
| const REVIEW_DECISIONS = new Set([ | ||
| 'APPROVED', | ||
| 'APPROVED_WITH_CONCERNS', | ||
| 'NEEDS_CHANGES', | ||
| 'BLOCKED', | ||
| 'PENDING', | ||
| ]); | ||
| const APPROVED_REVIEW_DECISIONS = new Set([ | ||
| 'APPROVED', | ||
| 'APPROVED_WITH_CONCERNS', | ||
| ]); | ||
| class ClassicChangeCloseoutService { | ||
| constructor(fileService) { | ||
| this.fileService = fileService; | ||
| } | ||
| async analyzeDocumentationContract(projectRoot, proposalPath) { | ||
| if (!(await this.fileService.exists(proposalPath))) { | ||
| return { | ||
| changeType: '', | ||
| impact: '', | ||
| updates: [], | ||
| archiveReady: false, | ||
| checks: [ | ||
| { | ||
| name: 'change.documentation_contract', | ||
| status: 'fail', | ||
| message: 'proposal.md is required before documentation impact can be assessed', | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| let document; | ||
| try { | ||
| document = (0, helpers_1.parseFrontmatterDocument)(await this.fileService.readFile(proposalPath)); | ||
| } | ||
| catch (error) { | ||
| return { | ||
| changeType: '', | ||
| impact: '', | ||
| updates: [], | ||
| archiveReady: false, | ||
| checks: [ | ||
| { | ||
| name: 'change.documentation_contract', | ||
| status: 'fail', | ||
| message: `proposal.md documentation contract cannot be parsed: ${error?.message || error}`, | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| const changeType = String(document.data?.change_type || '') | ||
| .trim() | ||
| .toLowerCase(); | ||
| const impact = String(document.data?.documentation_impact || '') | ||
| .trim() | ||
| .toLowerCase(); | ||
| const reason = String(document.data?.documentation_reason || '').trim(); | ||
| const updates = Array.isArray(document.data?.documentation_updates) | ||
| ? Array.from(new Set(document.data.documentation_updates | ||
| .map((item) => String(item || '') | ||
| .trim() | ||
| .replace(/\\/g, '/') | ||
| .replace(/^\.\//, '')) | ||
| .filter(Boolean))) | ||
| : []; | ||
| const checks = []; | ||
| const typeValid = CHANGE_TYPES.has(changeType); | ||
| checks.push({ | ||
| name: 'change.change_type', | ||
| status: typeValid ? 'pass' : 'fail', | ||
| message: typeValid | ||
| ? `Change type is ${changeType}` | ||
| : 'proposal.md change_type must be bugfix, feature, maintenance, or docs', | ||
| }); | ||
| const impactValid = DOCUMENTATION_IMPACTS.has(impact); | ||
| checks.push({ | ||
| name: 'change.documentation_impact', | ||
| status: impactValid ? 'pass' : 'fail', | ||
| message: impactValid | ||
| ? `Documentation impact is ${impact}` | ||
| : 'proposal.md documentation_impact must be none or required', | ||
| }); | ||
| const typeRequiresDocumentation = changeType === 'feature' || changeType === 'docs'; | ||
| const typeImpactValid = !typeRequiresDocumentation || impact === 'required'; | ||
| checks.push({ | ||
| name: 'change.documentation_policy', | ||
| status: typeImpactValid ? 'pass' : 'fail', | ||
| message: typeImpactValid | ||
| ? typeRequiresDocumentation | ||
| ? `${changeType} changes require real documentation updates` | ||
| : `${changeType || 'unclassified'} changes may record no documentation impact when justified` | ||
| : `${changeType} changes must set documentation_impact to required`, | ||
| }); | ||
| const noneContractValid = impact !== 'none' || (updates.length === 0 && reason.length > 0); | ||
| checks.push({ | ||
| name: 'change.documentation_none_reason', | ||
| status: noneContractValid ? 'pass' : 'fail', | ||
| message: noneContractValid | ||
| ? impact === 'none' | ||
| ? 'No documentation impact is justified and no update paths are declared' | ||
| : 'Documentation updates are required' | ||
| : impact === 'none' && updates.length > 0 | ||
| ? 'documentation_updates must be empty when documentation_impact is none' | ||
| : 'documentation_reason is required when documentation_impact is none', | ||
| }); | ||
| const requiredContractValid = impact !== 'required' || updates.length > 0; | ||
| checks.push({ | ||
| name: 'change.documentation_updates', | ||
| status: requiredContractValid ? 'pass' : 'fail', | ||
| message: requiredContractValid | ||
| ? impact === 'required' | ||
| ? `${updates.length} real documentation update path(s) declared` | ||
| : 'No documentation update paths are required' | ||
| : 'documentation_updates must name at least one real project document when documentation_impact is required', | ||
| }); | ||
| for (const update of updates) { | ||
| const validation = await this.validateDocumentationPath(projectRoot, update); | ||
| checks.push({ | ||
| name: `change.documentation_updates.${update}`, | ||
| status: validation.ready ? 'pass' : 'fail', | ||
| message: validation.message, | ||
| }); | ||
| } | ||
| return { | ||
| changeType, | ||
| impact, | ||
| updates, | ||
| archiveReady: checks.every(check => check.status !== 'fail'), | ||
| checks, | ||
| }; | ||
| } | ||
| async analyzeReview(reviewPath) { | ||
| if (!(await this.fileService.exists(reviewPath))) { | ||
| return { | ||
| decision: '', | ||
| checklistComplete: false, | ||
| archiveReady: false, | ||
| checks: [ | ||
| { | ||
| name: 'review.md', | ||
| status: 'fail', | ||
| message: 'review.md is required for classic change closeout', | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| let document; | ||
| try { | ||
| document = (0, helpers_1.parseFrontmatterDocument)(await this.fileService.readFile(reviewPath)); | ||
| } | ||
| catch (error) { | ||
| return { | ||
| decision: '', | ||
| checklistComplete: false, | ||
| archiveReady: false, | ||
| checks: [ | ||
| { | ||
| name: 'review.md', | ||
| status: 'fail', | ||
| message: `review.md cannot be parsed: ${error?.message || error}`, | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| const decision = String(document.data?.decision || '') | ||
| .trim() | ||
| .toUpperCase(); | ||
| const decisionValid = REVIEW_DECISIONS.has(decision); | ||
| const decisionApproved = APPROVED_REVIEW_DECISIONS.has(decision); | ||
| const checklistMatches = Array.from(document.content.matchAll(/^\s*-\s+\[([ xX])\]\s+.+$/gm)); | ||
| const checklistComplete = checklistMatches.length > 0 && | ||
| checklistMatches.every(match => match[1].toLowerCase() === 'x'); | ||
| const checks = [ | ||
| { | ||
| name: 'review.md.decision', | ||
| status: decisionApproved | ||
| ? decision === 'APPROVED_WITH_CONCERNS' | ||
| ? 'warn' | ||
| : 'pass' | ||
| : 'fail', | ||
| message: decisionValid | ||
| ? decisionApproved | ||
| ? `Classic change review decision is ${decision}` | ||
| : `Classic change review remains ${decision}` | ||
| : 'review.md decision must be APPROVED, APPROVED_WITH_CONCERNS, NEEDS_CHANGES, BLOCKED, or PENDING', | ||
| }, | ||
| { | ||
| name: 'review.md.checklist', | ||
| status: checklistComplete ? 'pass' : 'fail', | ||
| message: checklistComplete | ||
| ? 'Classic change review checklist is complete' | ||
| : 'review.md must contain a fully checked lightweight review checklist', | ||
| }, | ||
| ]; | ||
| return { | ||
| decision, | ||
| checklistComplete, | ||
| archiveReady: decisionApproved && checklistComplete, | ||
| checks, | ||
| }; | ||
| } | ||
| async analyzePluginGates(changePath, activatedSteps, workflow) { | ||
| const checks = []; | ||
| if (activatedSteps.includes('stitch_design_review')) { | ||
| const approvalPath = path.join(changePath, 'artifacts', 'stitch', 'approval.json'); | ||
| const exists = await this.fileService.exists(approvalPath); | ||
| let approved = false; | ||
| if (exists) { | ||
| const approval = await this.fileService.readJSON(approvalPath); | ||
| approved = | ||
| approval.step === 'stitch_design_review' && | ||
| approval.status === 'approved' && | ||
| typeof approval.preview_url === 'string' && | ||
| approval.preview_url.trim().length > 0 && | ||
| typeof approval.submitted_at === 'string' && | ||
| approval.submitted_at.trim().length > 0; | ||
| } | ||
| checks.push({ | ||
| name: 'stitch.approval', | ||
| status: approved ? 'pass' : 'fail', | ||
| message: approved | ||
| ? 'Stitch design review is approved with complete evidence' | ||
| : 'Stitch design review approval with preview URL and submission timestamp is required', | ||
| }); | ||
| } | ||
| const checkpointSteps = activatedSteps.filter(step => step === 'checkpoint_ui_review' || step === 'checkpoint_flow_check'); | ||
| if (checkpointSteps.length > 0) { | ||
| const checkpointDir = path.join(changePath, 'artifacts', 'checkpoint'); | ||
| const gatePath = path.join(checkpointDir, 'gate.json'); | ||
| const gateExists = await this.fileService.exists(gatePath); | ||
| let ready = false; | ||
| if (gateExists) { | ||
| const gate = await this.fileService.readJSON(gatePath); | ||
| ready = | ||
| gate.plugin === 'checkpoint' && | ||
| gate.status === 'passed' && | ||
| gate.evidence?.status === 'complete' && | ||
| checkpointSteps.every(step => gate.steps?.[step]?.status === 'passed' && | ||
| gate.evidence?.by_step?.[step]?.status === 'complete') && | ||
| ((await this.fileService.exists(path.join(checkpointDir, 'result.json'))) || | ||
| (await this.fileService.exists(path.join(checkpointDir, 'summary.md')))); | ||
| } | ||
| checks.push({ | ||
| name: 'checkpoint.gate', | ||
| status: ready ? 'pass' : 'fail', | ||
| message: ready | ||
| ? 'Checkpoint gate and evidence are complete' | ||
| : 'Checkpoint gate, per-step evidence, and result or summary are required', | ||
| }); | ||
| } | ||
| const externalCapabilities = workflow | ||
| .getPluginCapabilities() | ||
| .filter(capability => capability.plugin !== 'stitch' && | ||
| capability.plugin !== 'checkpoint' && | ||
| activatedSteps.includes(capability.step)); | ||
| const stepsByPlugin = new Map(); | ||
| for (const capability of externalCapabilities) { | ||
| stepsByPlugin.set(capability.plugin, [ | ||
| ...(stepsByPlugin.get(capability.plugin) || []), | ||
| capability.step, | ||
| ]); | ||
| } | ||
| for (const [pluginName, pluginSteps] of stepsByPlugin) { | ||
| const pluginDir = path.join(changePath, 'artifacts', pluginName); | ||
| const gatePath = path.join(pluginDir, 'gate.json'); | ||
| const gateExists = await this.fileService.exists(gatePath); | ||
| let ready = false; | ||
| if (gateExists) { | ||
| const gate = await this.fileService.readJSON(gatePath); | ||
| ready = | ||
| gate.plugin === pluginName && | ||
| (gate.status === 'passed' || gate.status === 'approved') && | ||
| pluginSteps.every(step => { | ||
| const status = gate.steps?.[step]?.status; | ||
| return status === 'passed' || status === 'approved'; | ||
| }) && | ||
| ((await this.fileService.exists(path.join(pluginDir, 'result.json'))) || | ||
| (await this.fileService.exists(path.join(pluginDir, 'summary.md')))); | ||
| } | ||
| checks.push({ | ||
| name: `${pluginName}.gate`, | ||
| status: ready ? 'pass' : 'fail', | ||
| message: ready | ||
| ? `${pluginName} gate and artifacts are complete` | ||
| : `${pluginName} approved gate plus result or summary is required`, | ||
| }); | ||
| } | ||
| return { | ||
| archiveReady: checks.every(check => check.status !== 'fail'), | ||
| checks, | ||
| }; | ||
| } | ||
| deriveCloseoutState(state, input) { | ||
| const completed = new Set(state.completed || []); | ||
| const setStep = (step, ready) => { | ||
| if (ready) | ||
| completed.add(step); | ||
| else | ||
| completed.delete(step); | ||
| }; | ||
| setStep('proposal_complete', input.proposalReady && input.documentationReady); | ||
| setStep('tasks_complete', input.tasksReady); | ||
| setStep('implementation_complete', input.tasksReady); | ||
| setStep('tests_passed', input.verificationReady); | ||
| setStep('verification_passed', input.verificationReady && input.reviewReady); | ||
| const ready = input.proposalReady && | ||
| input.tasksReady && | ||
| input.verificationReady && | ||
| input.reviewReady && | ||
| input.documentationReady && | ||
| input.pluginsReady; | ||
| const managedSteps = [ | ||
| 'proposal_complete', | ||
| 'tasks_complete', | ||
| 'implementation_complete', | ||
| 'tests_passed', | ||
| 'verification_passed', | ||
| 'archived', | ||
| ]; | ||
| const pending = managedSteps.filter(step => !completed.has(step)); | ||
| const status = ready | ||
| ? 'ready_to_archive' | ||
| : !input.proposalReady || !input.documentationReady | ||
| ? 'draft' | ||
| : !input.tasksReady | ||
| ? 'implementing' | ||
| : 'verifying'; | ||
| return { | ||
| ...state, | ||
| status, | ||
| current_step: status, | ||
| completed: Array.from(completed).sort((left, right) => left.localeCompare(right)), | ||
| pending, | ||
| blocked_by: ready ? [] : ['classic_change_closeout_incomplete'], | ||
| }; | ||
| } | ||
| async validateDocumentationPath(projectRoot, relativePath) { | ||
| if (!relativePath || path.isAbsolute(relativePath)) { | ||
| return { | ||
| ready: false, | ||
| message: `Documentation update path must be a relative project path: ${relativePath || '(empty)'}`, | ||
| }; | ||
| } | ||
| const normalized = relativePath.replace(/\\/g, '/'); | ||
| const lower = normalized.toLowerCase(); | ||
| if (lower.startsWith('docs/project/changes/') || | ||
| lower.startsWith('.ospec/docs/project/changes/')) { | ||
| return { | ||
| ready: false, | ||
| message: `Generated archive summaries do not satisfy feature documentation: ${normalized}`, | ||
| }; | ||
| } | ||
| if (lower.startsWith('.ospec/changes/') || | ||
| lower.startsWith('changes/')) { | ||
| return { | ||
| ready: false, | ||
| message: `Change protocol files are not durable project documentation: ${normalized}`, | ||
| }; | ||
| } | ||
| const resolved = path.resolve(projectRoot, ...normalized.split('/')); | ||
| const relative = path.relative(projectRoot, resolved); | ||
| if (!relative || | ||
| relative === '..' || | ||
| relative.startsWith(`..${path.sep}`) || | ||
| path.isAbsolute(relative)) { | ||
| return { | ||
| ready: false, | ||
| message: `Documentation update path escapes the project root: ${normalized}`, | ||
| }; | ||
| } | ||
| const segments = lower.split('/'); | ||
| const inDocumentationTree = segments.some(segment => ['docs', 'documentation', 'knowledge'].includes(segment)); | ||
| const proseDocument = /\.(md|mdx|rst|adoc)$/i.test(normalized); | ||
| const namedDocument = /(^|\/)(readme|changelog|skill)(\.[^/]+)?$/i.test(normalized) || | ||
| /(^|\/)(openapi|asyncapi)(\.[^/]+)?$/i.test(normalized); | ||
| const structuredDocumentation = inDocumentationTree && /\.(yaml|yml|json)$/i.test(normalized); | ||
| const documentationLike = proseDocument || namedDocument || structuredDocumentation; | ||
| if (!documentationLike) { | ||
| return { | ||
| ready: false, | ||
| message: `Declared documentation update is not a recognized durable document: ${normalized}`, | ||
| }; | ||
| } | ||
| const stat = await fs_1.promises.stat(resolved).catch(() => null); | ||
| if (!stat) { | ||
| return { | ||
| ready: false, | ||
| message: `Declared documentation update does not exist: ${normalized}`, | ||
| }; | ||
| } | ||
| if (!stat.isFile()) { | ||
| return { | ||
| ready: false, | ||
| message: `Declared documentation update is not a file: ${normalized}`, | ||
| }; | ||
| } | ||
| return { | ||
| ready: true, | ||
| message: `Real project documentation update is present: ${normalized}`, | ||
| }; | ||
| } | ||
| } | ||
| exports.ClassicChangeCloseoutService = ClassicChangeCloseoutService; |
@@ -31,4 +31,4 @@ --- | ||
| - تعامل مع خطوات built-in quality policy المفعّلة مثل `tdd_cycle` و`root_cause_debug` و`verification_evidence` كـ `optional_steps` خاضعة لـ archive gate؛ غطّها في `tasks.md` و`verification.md` وملفات evidence المطابقة قبل closeout | ||
| - استخدم `ospec new` / `ospec-change` للتغييرات الصغيرة والروتينية، وأبقها على تدفق 1.0 السريع: `proposal.md` و`tasks.md` والتنفيذ و`verification.md` و`review.md` و`state.json` | ||
| - استخدم `ospec goal` / `ospec-goal` للعمل المعقد الذي يحتاج `design.md` و`implementation-plan.md` وtask graph وdocument review وworker/reviewer handoff وevidence gates | ||
| - استخدم `ospec change` / `ospec-change` عندما يختار المستخدم Change، ويبقى `ospec new` alias. أبقها على تدفق 1.0 السريع بغض النظر عن التعقيد أو flags أو عدد الملفات أو المخاطر أو حجم الدفعة: `proposal.md` و`tasks.md` والتنفيذ و`verification.md` و`review.md` و`state.json` | ||
| - استخدم `ospec goal` / `ospec-goal` فقط عندما يختار المستخدم Goal صراحة | ||
| - طبقة التحكم `ospec execute …` (bootstrap وdoc-review وdispatch وlaunch وreview وworktree وfinish وcollect وretry وsync) وكل artifacts الخاصة بـ goal تنتمي إلى `workflow_profile_id: goal`. وبالنسبة لـ `workflow_profile_id: change`، التزم بالتدفق السريع الكلاسيكي — لا تقرأ ولا تشغّل طبقة execute أو artifacts الخاصة بـ goal؛ حرّر `proposal.md` و`tasks.md`، ونفّذ، وسجّل `verification.md` و`review.md`، ثم أغلق بـ `ospec verify` و`ospec finalize` — ما لم يطلب المستخدم صراحةً تنفيذ agent/worker على هذا الـ change | ||
@@ -35,0 +35,0 @@ - عند تنفيذ goal بمساعدة AI، لا تطلب من المستخدم كتابة `design.md` أو `implementation-plan.md` يدوياً؛ أنشئهما أو حدّثهما من المتطلب و`proposal.md` وسياق المشروع قبل اشتقاق `artifacts/agents/task-graph.json` أو تعديل `tasks.md` أو الكود |
@@ -29,4 +29,4 @@ --- | ||
| - تعامل مع خطوات built-in quality policy المفعّلة مثل `tdd_cycle` و`root_cause_debug` و`verification_evidence` كـ `optional_steps` خاضعة لـ archive gate؛ غطّها في `tasks.md` و`verification.md` وملفات evidence المطابقة قبل closeout | ||
| - استخدم `ospec new` / `ospec-change` للتغييرات الصغيرة والروتينية، وأبقها على تدفق 1.0 السريع: `proposal.md` و`tasks.md` والتنفيذ و`verification.md` و`review.md` و`state.json` | ||
| - استخدم `ospec goal` / `ospec-goal` للعمل المعقد الذي يحتاج `design.md` و`implementation-plan.md` وtask graph وdocument review وworker/reviewer handoff وevidence gates | ||
| - استخدم `ospec change` / `ospec-change` عندما يختار المستخدم Change، ويبقى `ospec new` alias. أبقها على تدفق 1.0 السريع بغض النظر عن التعقيد أو flags أو عدد الملفات أو المخاطر أو حجم الدفعة: `proposal.md` و`tasks.md` والتنفيذ و`verification.md` و`review.md` و`state.json` | ||
| - استخدم `ospec goal` / `ospec-goal` فقط عندما يختار المستخدم Goal صراحة | ||
| - طبقة التحكم `ospec execute …` (bootstrap وdoc-review وdispatch وlaunch وreview وworktree وfinish وcollect وretry وsync) وكل artifacts الخاصة بـ goal تنتمي إلى `workflow_profile_id: goal`. وبالنسبة لـ `workflow_profile_id: change`، التزم بالتدفق السريع الكلاسيكي — لا تقرأ ولا تشغّل طبقة execute أو artifacts الخاصة بـ goal؛ حرّر `proposal.md` و`tasks.md`، ونفّذ، وسجّل `verification.md` و`review.md`، ثم أغلق بـ `ospec verify` و`ospec finalize` — ما لم يطلب المستخدم صراحةً تنفيذ agent/worker على هذا الـ change | ||
@@ -43,3 +43,3 @@ - أثناء تنفيذ goal بمساعدة AI، أنشئ `design.md` أو حدّثه بعد `proposal.md` وقبل تعديل `implementation-plan.md` أو `tasks.md` أو الكود. في classic change لا تنشئ goal-only files ما لم يطلب المستخدم الترقية إلى goal صراحة | ||
| - يجب أن تتقارب عمليات review repair. ترث task downstream التي تعدل ملفات مشتركة التزامات regression من upstream المتعدية. تمثل الجولتان الافتراضيتان عتبة تقارب. يستمر التنفيذ عندما تتغير structured finding IDs. ويمكن أن يستمر المعرف نفسه فقط عندما يتغير كل من structured finding fingerprint وcode snapshot داخل repair scope المصرح به سابقا؛ أما تغيير الصياغة فقط أو الكود فقط أو التكرار الدقيق أو الدورة فيتوقف. عندما تكون final review بحالة `BLOCKED` يجب التوقف لحل blocker وعدم بدء grouped repair. لا ترفع الحدود لتجاوز evidence غير المتغيرة. | ||
| - يجب أن يكون انتظار native child محدوداً في كل harness. يعيد `wait_agent` في Codex/GPT وpolling لـ Claude Task وأي native wait آخر التحكم خلال 60 ثانية؛ هذا حد poll واحدة للـ controller وليس حد تشغيل child. حدّث كل child حي قبل `heartbeatDueAt`، وعند الاكتمال استخدم `loop finalize` الصادر مع action لحفظ evidence وresult ذرياً، ثم أعد tick بعد كل poll. يمكن للـ child الاستمرار عبر عدة polls حتى action deadline، وتوجد result grace محدودة بعد اكتمال evidence. عند غياب capacity يقتصر implementation batch على مهمتين من دون تقليل توازي review الآمن. | ||
| - يجب أن يكون انتظار native child محدوداً في كل harness. يعيد `wait_agent` في Codex/GPT وpolling لـ Claude Task وأي native wait آخر التحكم خلال 60 ثانية؛ هذا حد poll واحدة للـ controller وليس حد تشغيل child. حدّث كل child حي قبل `heartbeatDueAt`، وعند الاكتمال استخدم `loop finalize` الصادر مع action لحفظ evidence وresult ذرياً، ثم أعد tick بعد كل poll. يمكن للـ child الاستمرار عبر عدة polls حتى action deadline، وتوجد result grace محدودة بعد اكتمال evidence. عند غياب capacity يستخدم implementation التوازي الافتراضي وهو ثلاث مهام من دون تقليل توازي review الآمن. إذا كان harness يعرف بثقة capacity حالية أكبر للـ child فيمكنه ربطها بجلسة controller النشطة ورفع `maxParallel` وفقاً لذلك؛ لا تخمّن capacity ولا تعِد استخدام قيمة قديمة. | ||
| - تجدد كل controller poll ناجحة ومحدودة lease القصيرة لـ child حي ومطالب به من دون تمديد absolute deadline. ويمكن لـ poll تصل خلال نافذة wait محدودة قدرها 60 ثانية بعد حد lease القصيرة أن تجدد نفس item المطالب به؛ تبقى النتائج المتأخرة المباشرة مرفوضة، وتنتهي items المفقودة فعلا، ولا يتحرك absolute deadline. يجب تنفيذ prerequisite review المفقودة قبل إعادة محاولة dependent work القابل للإعادة. لا يقبل cross-task finding paths إضافية إلا إذا كانت مملوكة لمهام مكتملة ومعلنة في task graph؛ جمّد scope الكامل وأعد review للمالكين المتغيرين. ما دام أي cross-task owner مسجل غير معتمد، تسبق review أو repair الخاصة به أي implementation جديد أو retryable worker، مع بقاء reviewers الآخرين غير المتعارضين متوازيين. قبل full Docker Compose rebuild من دون أسماء خدمات، اقرأ release guidance للمشروع واستخدم أسماء خدمات صريحة عند وجود خدمات غير مرتبطة. | ||
@@ -66,3 +66,3 @@ - عند بدء أو استئناف active change واحد، استخدم `ospec execute bootstrap [changes/active/<change>]` لكتابة `artifacts/agents/bootstrap.json` و`artifacts/agents/bootstrap.md` مع project session brief snapshot، ثم اتبع الإجراء الآمن التالي المسجل فيه | ||
| - في Goal مملوكة لـ controller، استخدم `ospec loop tick [changes/active/<change>]` بعد اكتمال كل worker task لإنشاء مراجعة موحدة مرتبطة بـ executor provenance الحقيقي وحزمة محدودة النطاق. استخدم `ospec execute review ... --task <task-id>` مباشرةً فقط خارج controller Loop | ||
| - عند تفعيل `documentation_updates` في task graph، يجب أن تحتوي كل مهمة على المصفوفة (`[]` عند عدم الحاجة)، وأن يظهر كل مسار docs معلن أيضا في `target_files` للمهمة نفسها، وأن يملك دليلا على تغير فعلي بين dispatch وcomplete. يعد الحذف الذي تمت مراجعته تغييرا صالحا عندما يثبت evidence أن baseline موجودا أصبح مفقودا عند الإكمال. عبر محاولات repair المتعددة يقارن finalize أول baseline بالحالة النهائية المكتملة، ويشترط أن تطابق workspace أحدث evidence لمالك المسار المعلن، لذلك لا يكفي تغيير قديم ولا يمر التراجع اللاحق. تبقى التشغيلات القديمة بلا baseline متوافقة فقط عندما تظل الوثيقة المعلنة موجودة، وتعرض الدليل كغير متحقق. | ||
| - عند تفعيل `documentation_updates` في task graph، يجب أن تحتوي كل مهمة على المصفوفة (`[]` عند عدم الحاجة)، وأن يظهر كل مسار docs معلن أيضا في `target_files` للمهمة نفسها، وأن يملك دليلا على تغير فعلي بين dispatch وcomplete. يعد الحذف الذي تمت مراجعته تغييرا صالحا عندما يثبت evidence أن baseline موجودا أصبح مفقودا عند الإكمال. عبر محاولات repair المتعددة يقارن finalize أول baseline بالحالة النهائية المكتملة، ويشترط أن تطابق workspace أحدث evidence لمالك المسار المعلن، لذلك لا يكفي تغيير قديم ولا يمر التراجع اللاحق. إذا غير controller closeout مسارا معلنا بعد آخر worker dispatch، فلا تعتمد الحالة النهائية إلا مراجعة task لاحقة بحالة APPROVED، وexecutor provenance صالح، وtarget snapshot يطابق الملف الحالي تماما؛ ولا تحل هذه المراجعة محل meaningful-change chain. تبقى التشغيلات القديمة بلا baseline متوافقة فقط عندما تظل الوثيقة المعلنة موجودة، وتعرض الدليل كغير متحقق. | ||
| - بعد اعتماد task-level reviews واكتمال task graph، دع `ospec loop tick` التالي ينشئ final review في controller Loop؛ استخدم `ospec execute review` من دون `--task` فقط خارج controller Loop | ||
@@ -69,0 +69,0 @@ - بعد أن يحتوي 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 |
@@ -32,4 +32,4 @@ --- | ||
| - Treat activated built-in quality policy steps such as `tdd_cycle`, `root_cause_debug`, and `verification_evidence` as archive-gated `optional_steps`; cover them in `tasks.md`, `verification.md`, and matching evidence artifacts before closeout | ||
| - Use `ospec new` / `ospec-change` for routine small changes. These changes should stay on the classic fast flow: `proposal.md`, `tasks.md`, implementation, `verification.md`, `review.md`, and `state.json`. | ||
| - Use `ospec goal` / `ospec-goal` for complex work that needs design docs, implementation planning, task graph dispatch, document review, worker/reviewer handoff, or durable evidence gates. | ||
| - Use `ospec change` / `ospec-change` whenever the user selects a Change; `ospec new` remains an alias. Keep it on the classic fast flow regardless of complexity, flags, file count, risk, or batch size: `proposal.md`, `tasks.md`, implementation, `verification.md`, `review.md`, and `state.json`. | ||
| - Use `ospec goal` / `ospec-goal` only when the user explicitly selects a Goal. | ||
| - The `ospec execute …` controller layer (bootstrap, doc-review, dispatch, launch, review, worktree, finish, collect, retry, sync) and every goal-only artifact belong to `workflow_profile_id: goal`. For `workflow_profile_id: change`, keep the classic fast flow — do not read or run the execute layer or goal artifacts; edit `proposal.md` and `tasks.md`, implement, record `verification.md` and `review.md`, then close out with `ospec verify` and `ospec finalize` — unless the user explicitly asks for agent/worker execution on this change. | ||
@@ -36,0 +36,0 @@ - In goal execution, do not ask the user to hand-write `design.md` or `implementation-plan.md`; draft or update them from the requirement, `proposal.md`, and project context before deriving `artifacts/agents/task-graph.json`, editing `tasks.md`, or editing code. |
@@ -31,4 +31,4 @@ --- | ||
| - Treat activated built-in quality policy steps such as `tdd_cycle`, `root_cause_debug`, and `verification_evidence` as archive-gated `optional_steps`; cover them in `tasks.md`, `verification.md`, and matching evidence artifacts before closeout | ||
| - Use `ospec new` / `ospec-change` for routine small changes. Keep them on the classic fast flow: `proposal.md`, `tasks.md`, implementation, `verification.md`, `review.md`, and `state.json`. | ||
| - Use `ospec goal` / `ospec-goal` for complex work that needs design docs, implementation planning, task graph dispatch, document review, worker/reviewer handoff, or durable evidence gates. | ||
| - Use `ospec change` / `ospec-change` whenever the user selects a Change; `ospec new` remains an alias. Keep it on the classic fast flow regardless of complexity, flags, file count, risk, or batch size: `proposal.md`, `tasks.md`, implementation, `verification.md`, `review.md`, and `state.json`. | ||
| - Use `ospec goal` / `ospec-goal` only when the user explicitly selects a Goal. | ||
| - The `ospec execute …` controller layer (bootstrap, doc-review, dispatch, launch, review, worktree, finish, collect, retry, sync) and every goal-only artifact belong to `workflow_profile_id: goal`. For `workflow_profile_id: change`, keep the classic fast flow — do not read or run the execute layer or goal artifacts; edit `proposal.md` and `tasks.md`, implement, record `verification.md` and `review.md`, then close out with `ospec verify` and `ospec finalize` — unless the user explicitly asks for agent/worker execution on this change. | ||
@@ -45,3 +45,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. | ||
| - Review repair must converge. A downstream task that shares files inherits transitive upstream regression obligations. The default two-round values are convergence thresholds. Continue when structured finding IDs change. A stable ID may also continue only when both its structured finding fingerprint and its prior authorized repair-scope code snapshot changed; wording-only or code-only churn, exact repeats, and cycles stop. A blocked final review stops for blocker resolution; it never enters grouped repair. Do not raise a limit to bypass unchanged evidence. | ||
| - Native child waiting is bounded on every harness. Codex/GPT `wait_agent`, Claude Task polling, and other native waits return within 60 seconds; this limits one controller poll, not the child runtime. Refresh every live child before `heartbeatDueAt`, persist each completed result with its emitted `loop finalize` command, and re-tick after every poll. A live child may continue across polls up to its action deadline, and evidence-complete work receives a bounded result grace period. Unknown capacity caps implementation batches at two but does not reduce safe review parallelism. | ||
| - Native child waiting is bounded on every harness. Codex/GPT `wait_agent`, Claude Task polling, and other native waits return within 60 seconds; this limits one controller poll, not the child runtime. Refresh every live child before `heartbeatDueAt`, persist each completed result with its emitted `loop finalize` command, and re-tick after every poll. A live child may continue across polls up to its action deadline, and evidence-complete work receives a bounded result grace period. Unknown capacity uses the default implementation concurrency of three without reducing safe review parallelism. A harness that authoritatively knows a larger current child capacity may report it for the active controller session and raise `maxParallel` accordingly; never guess or reuse stale capacity. | ||
| - A successful bounded controller poll renews the short lease for an already-claimed live child without extending its absolute deadline. A poll arriving within one bounded 60-second wait after the short-lease boundary may renew that same claimed item; direct late results remain rejected, genuinely abandoned items still expire, and the absolute deadline never moves. Retryable dependent work must schedule missing prerequisite reviews before worker retry. Cross-task finding scope is valid only when every additional path belongs to a declared completed task; freeze the complete scope and re-review changed owners. While any recorded cross-task owner lacks approval, its review or repair precedes new implementation and retryable worker work, while other conflict-safe reviewers may remain parallel. Before any unscoped full Docker Compose rebuild, inspect project release guidance and prefer explicit service names when unrelated services exist. | ||
@@ -75,3 +75,3 @@ - When starting or resuming one active change, use `ospec execute bootstrap [changes/active/<change>]` to write `artifacts/agents/bootstrap.json` and `artifacts/agents/bootstrap.md` with the project session brief snapshot, then follow its next safe action | ||
| - 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. | ||
| - When a task graph includes `documentation_updates`, include that array on every task (`[]` when none); every declared docs path must also appear in that task's `target_files` and have dispatch-to-completion meaningful-change evidence. A reviewed deletion is valid when evidence proves an existing baseline became missing. Across repair attempts, finalize compares the first baseline with the final completed state and requires the workspace to match the latest declared-owner evidence, so an old change or later reversion cannot pass. Legacy runs without a captured baseline remain compatible only when the declared document still exists and must report the evidence as unverified. | ||
| - When a task graph includes `documentation_updates`, include that array on every task (`[]` when none); every declared docs path must also appear in that task's `target_files` and have dispatch-to-completion meaningful-change evidence. A reviewed deletion is valid when evidence proves an existing baseline became missing. Across repair attempts, finalize compares the first baseline with the final completed state and requires the workspace to match the latest declared-owner evidence, so an old change or later reversion cannot pass. If controller closeout changes a declared path after the last worker dispatch, only a later APPROVED task review with valid executor provenance and an exact current target snapshot may authorize that final state; it does not replace the meaningful-change chain. Legacy runs without a captured baseline remain compatible only when the declared document still exists and must report the evidence as unverified. | ||
| - For goals, do not archive while `artifacts/agents/task-graph.json` has unresolved task statuses, invalid dependencies, missing execution details, or top-level `status` other than `completed` | ||
@@ -78,0 +78,0 @@ - Complete each task's single combined review (`artifacts/reviews/tasks/<task-id>/review.md`), then complete the single final `artifacts/reviews/final-review.md`; unresolved task-level or final review decisions block archive |
@@ -31,4 +31,4 @@ --- | ||
| - `tdd_cycle`、`root_cause_debug`、`verification_evidence` など有効化された built-in quality policy steps は、archive-gated `optional_steps` として扱う。closeout 前に `tasks.md`、`verification.md`、対応する evidence artifacts で coverage を記録する | ||
| - 小さな通常変更には `ospec new` / `ospec-change` を使い、1.0 の高速フロー(`proposal.md`、`tasks.md`、実装、`verification.md`、`review.md`、`state.json`)に留める | ||
| - 複雑な作業には `ospec goal` / `ospec-goal` を使い、`design.md`、`implementation-plan.md`、task graph、document review、worker/reviewer handoff、evidence gates を有効にする | ||
| - ユーザーが Change を選択した場合は `ospec change` / `ospec-change` を使い、`ospec new` は alias として残す。複雑さ、flags、ファイル数、risk、batch size に関係なく 1.0 の高速フロー(`proposal.md`、`tasks.md`、実装、`verification.md`、`review.md`、`state.json`)に留める | ||
| - ユーザーが Goal を明示的に選択した場合だけ `ospec goal` / `ospec-goal` を使う | ||
| - `ospec execute …` コントローラ層(bootstrap、doc-review、dispatch、launch、review、worktree、finish、collect、retry、sync)と goal 専用 artifacts はすべて `workflow_profile_id: goal` に属する。`workflow_profile_id: change` では、クラシックな高速フローを維持し、execute 層や goal artifacts を読まず・実行せず、`proposal.md` と `tasks.md` を編集し、実装し、`verification.md` と `review.md` を記録してから `ospec verify` と `ospec finalize` で閉じる——ユーザーがこの change での agent/worker 実行を明示的に求めない限り | ||
@@ -35,0 +35,0 @@ - AI 支援で goal を進める場合、ユーザーに `design.md` や `implementation-plan.md` の手書きを求めない。要件、`proposal.md`、プロジェクト文脈からそれらを作成または更新してから `artifacts/agents/task-graph.json` を導出し、`tasks.md` やコードを編集する |
@@ -29,4 +29,4 @@ --- | ||
| - `tdd_cycle`、`root_cause_debug`、`verification_evidence` など有効化された built-in quality policy steps は、archive-gated `optional_steps` として扱う。closeout 前に `tasks.md`、`verification.md`、対応する evidence artifacts で coverage を記録する | ||
| - 小さな通常変更には `ospec new` / `ospec-change` を使い、1.0 の高速フロー(`proposal.md`、`tasks.md`、実装、`verification.md`、`review.md`、`state.json`)に留める | ||
| - 複雑な作業には `ospec goal` / `ospec-goal` を使い、`design.md`、`implementation-plan.md`、task graph、document review、worker/reviewer handoff、evidence gates を有効にする | ||
| - ユーザーが Change を選択した場合は `ospec change` / `ospec-change` を使い、`ospec new` は alias として残す。複雑さ、flags、ファイル数、risk、batch size に関係なく 1.0 の高速フロー(`proposal.md`、`tasks.md`、実装、`verification.md`、`review.md`、`state.json`)に留める | ||
| - ユーザーが Goal を明示的に選択した場合だけ `ospec goal` / `ospec-goal` を使う | ||
| - `ospec execute …` コントローラ層(bootstrap、doc-review、dispatch、launch、review、worktree、finish、collect、retry、sync)と goal 専用 artifacts はすべて `workflow_profile_id: goal` に属する。`workflow_profile_id: change` では、クラシックな高速フローを維持し、execute 層や goal artifacts を読まず・実行せず、`proposal.md` と `tasks.md` を編集し、実装し、`verification.md` と `review.md` を記録してから `ospec verify` と `ospec finalize` で閉じる——ユーザーがこの change での agent/worker 実行を明示的に求めない限り | ||
@@ -43,3 +43,3 @@ - AI 支援で goal を進める場合は、`proposal.md` の後、`implementation-plan.md`、`tasks.md`、コードを編集する前に `design.md` を作成または更新する。classic change では、ユーザーが明示的に goal へ昇格させない限り goal-only files を作成しない | ||
| - review repair は収束させる。共有ファイルを変更する downstream task は推移的 upstream の regression obligation を継承する。既定の 2 round は収束しきい値であり、structured finding ID が変化すれば自動続行する。同じ ID でも structured finding fingerprint と直前に許可された repair scope 内の code snapshot が両方変化した場合だけ続行できる。文言だけ、code だけの churn、完全な反復、cycle は停止する。final review が `BLOCKED` の場合は blocker の解決まで停止し、grouped repair に進めない。変化しない evidence を回避するために上限を引き上げてはならない。 | ||
| - すべての harness で native child の待機を bounded にする。Codex/GPT の `wait_agent`、Claude Task polling、その他の native wait は 60 秒以内に戻る。この 60 秒は controller poll 1 回の上限であり、child runtime の上限ではない。各 live child を `heartbeatDueAt` 前に更新し、完了時は action の `loop finalize` で evidence と result を atomic に保存して poll ごとに再 tick する。child は action deadline まで複数 poll にまたがって実行でき、evidence 完了後には bounded result grace がある。capacity 不明時は implementation batch を 2 件までに制限するが、安全な review 並列性は維持する。 | ||
| - すべての harness で native child の待機を bounded にする。Codex/GPT の `wait_agent`、Claude Task polling、その他の native wait は 60 秒以内に戻る。この 60 秒は controller poll 1 回の上限であり、child runtime の上限ではない。各 live child を `heartbeatDueAt` 前に更新し、完了時は action の `loop finalize` で evidence と result を atomic に保存して poll ごとに再 tick する。child は action deadline まで複数 poll にまたがって実行でき、evidence 完了後には bounded result grace がある。capacity 不明時の implementation は既定の並列数 3 を使用し、安全な review 並列性は維持する。harness が現在のより大きな child capacity を確実に把握できる場合は active controller session に結び付けて報告し、必要に応じて `maxParallel` を引き上げられるが、capacity を推測したり古い値を再利用してはならない。 | ||
| - 成功した bounded controller poll は、claim 済みの live child の短期 lease を更新するが、absolute deadline は延長しない。短期 lease 境界から bounded な 60 秒 wait 以内に到着した poll は同じ claim 済み item を更新できるが、期限切れ result の直接送信は拒否し、実際に失われた item は expire し、absolute deadline は動かさない。retryable な dependent work は worker retry より先に不足する prerequisite review を実行する。cross-task finding の追加 path は task graph の完了済み owner に属する場合だけ許可し、完全な scope を snapshot 化して変更された owner を再 review する。記録済み cross-task owner が未承認の間、その review または repair を新しい implementation と retryable worker より優先し、競合しない他の reviewer は並列実行できる。service 未指定の full Docker Compose rebuild の前には project release guidance を確認し、無関係な service があれば明示的な service 名を使用する。 | ||
@@ -65,3 +65,3 @@ - one active change を開始または再開するときは、`ospec execute bootstrap [changes/active/<change>]` で project session brief snapshot を含む `artifacts/agents/bootstrap.json` と `artifacts/agents/bootstrap.md` を書き、そこにある次の安全な action に従う | ||
| - controller-owned Goal では、各 worker task 完了後に `ospec loop tick [changes/active/<change>]` で実 executor provenance に結び付いた統合 review と task-scoped package を作成する。`ospec execute review ... --task <task-id>` を直接使うのは non-controller workflow のみとする | ||
| - task graph で `documentation_updates` を有効にした場合、各 task に配列を持たせ(不要なら `[]`)、宣言した docs path を同じ task の `target_files` に含め、dispatch から complete までの有意な content change evidence を残す。既存 baseline が完了時に missing になったことを evidence が証明する reviewed deletion は有効な変更である。複数 repair attempt では finalize が最初の baseline と最後の完了状態を比較し、workspace がその path の最新 declared owner evidence と一致することを要求するため、古い変更や後続の reversion は通過しない。baseline のない旧 run は宣言文書が現在も存在する場合だけ互換性を保ち、未検証と表示する。 | ||
| - task graph で `documentation_updates` を有効にした場合、各 task に配列を持たせ(不要なら `[]`)、宣言した docs path を同じ task の `target_files` に含め、dispatch から complete までの有意な content change evidence を残す。既存 baseline が完了時に missing になったことを evidence が証明する reviewed deletion は有効な変更である。複数 repair attempt では finalize が最初の baseline と最後の完了状態を比較し、workspace がその path の最新 declared owner evidence と一致することを要求するため、古い変更や後続の reversion は通過しない。controller closeout が最後の worker dispatch 後に宣言 path を変更した場合、その最終状態を許可できるのは、より後に実行され、executor provenance が有効で、現在の target snapshot と完全一致する APPROVED task review だけであり、meaningful-change chain の代わりにはならない。baseline のない旧 run は宣言文書が現在も存在する場合だけ互換性を保ち、未検証と表示する。 | ||
| - task graph 完了後の final review も controller Loop の次の `ospec loop tick` で発行する。`--task` なしの `ospec execute review` を直接使うのは non-controller workflow のみとする | ||
@@ -68,0 +68,0 @@ - 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 を作成する |
@@ -32,4 +32,4 @@ --- | ||
| - 将已激活的内建质量策略步骤(如 `tdd_cycle`、`root_cause_debug`、`verification_evidence`)视为受归档门禁约束的 `optional_steps`;收尾前必须在 `tasks.md`、`verification.md` 和对应 evidence artifacts 中覆盖 | ||
| - 小功能和常规改动使用 `ospec new` / `ospec-change`,保持 1.0 快速流程:`proposal.md`、`tasks.md`、实现、`verification.md`、`review.md` 和 `state.json` | ||
| - 复杂工作使用 `ospec goal` / `ospec-goal`,才启用 `design.md`、`implementation-plan.md`、task graph、文档 review、worker/reviewer 交接和 evidence 门禁 | ||
| - 用户选择 Change 时使用 `ospec change` / `ospec-change`,`ospec new` 保留为别名;不因复杂度、flags、文件数量、风险或批量任务升级,始终保持 1.0 快速流程:`proposal.md`、`tasks.md`、实现、`verification.md`、`review.md` 和 `state.json` | ||
| - 只有用户明确选择 Goal 时才使用 `ospec goal` / `ospec-goal` | ||
| - `ospec execute …` 控制层(bootstrap、doc-review、dispatch、launch、review、worktree、finish、collect、retry、sync)和所有 goal-only artifacts 都属于 `workflow_profile_id: goal`。对 `workflow_profile_id: change`,保持经典快速流程——不要读取或运行 execute 层或 goal artifacts;编辑 `proposal.md` 和 `tasks.md`、实现、记录 `verification.md` 和 `review.md`,再用 `ospec verify` 和 `ospec finalize` 收尾——除非用户明确要求对这个 change 做 agent/worker 执行 | ||
@@ -36,0 +36,0 @@ - AI 辅助执行 goal 时,不要求用户手写 `design.md` 或 `implementation-plan.md`;必须先基于需求、`proposal.md` 和项目上下文起草或更新它们,再推导 `artifacts/agents/task-graph.json`、编辑 `tasks.md` 或代码 |
@@ -31,4 +31,4 @@ --- | ||
| - 将已激活的内建质量策略步骤(如 `tdd_cycle`、`root_cause_debug`、`verification_evidence`)视为受归档门禁约束的 `optional_steps`;收尾前必须在 `tasks.md`、`verification.md` 和对应 evidence artifacts 中覆盖 | ||
| - 小功能和常规改动使用 `ospec new` / `ospec-change`,保持 1.0 快速流程:`proposal.md`、`tasks.md`、实现、`verification.md`、`review.md` 和 `state.json` | ||
| - 复杂工作使用 `ospec goal` / `ospec-goal`,才启用 `design.md`、`implementation-plan.md`、task graph、文档 review、worker/reviewer 交接和 evidence 门禁 | ||
| - 用户选择 Change 时使用 `ospec change` / `ospec-change`,`ospec new` 保留为别名;不因复杂度、flags、文件数量、风险或批量任务升级,始终保持 1.0 快速流程:`proposal.md`、`tasks.md`、实现、`verification.md`、`review.md` 和 `state.json` | ||
| - 只有用户明确选择 Goal 时才使用 `ospec goal` / `ospec-goal` | ||
| - `ospec execute …` 控制层(bootstrap、doc-review、dispatch、launch、review、worktree、finish、collect、retry、sync)和所有 goal-only artifacts 都属于 `workflow_profile_id: goal`。对 `workflow_profile_id: change`,保持经典快速流程——不要读取或运行 execute 层或 goal artifacts;编辑 `proposal.md` 和 `tasks.md`、实现、记录 `verification.md` 和 `review.md`,再用 `ospec verify` 和 `ospec finalize` 收尾——除非用户明确要求对这个 change 做 agent/worker 执行 | ||
@@ -45,3 +45,3 @@ - AI 辅助执行 goal 时,必须在完成 `proposal.md` 后、编辑 `implementation-plan.md`、`tasks.md` 或代码前,先起草或更新 `design.md`。执行经典 change 时,不要创建 goal-only 文件,除非用户明确升级为 goal | ||
| - review repair 必须收敛。共享文件的下游任务要继承传递上游的回归义务;默认两轮是收敛阈值。结构化 finding ID 变化时自动继续;同一 ID 只有在结构化 finding 指纹与上一轮授权 repair scope 内的代码快照同时变化时也可继续。只改措辞、只改代码、精确重复或循环都要停止。final review 为 `BLOCKED` 时必须停下解决 blocker,不得进入 grouped repair。不得通过提高上限来绕过未变化的证据。 | ||
| - 所有 harness 的 native child 等待都必须有界。Codex/GPT 的 `wait_agent`、Claude Task 轮询以及其它原生等待必须在 60 秒内返回;60 秒只限制一次 controller poll,不是 child 的执行上限。每个 live child 都要在 `heartbeatDueAt` 前续租,完成后用 action 给出的 `loop finalize` 原子提交证据和结果,并在每轮 poll 后重新 tick。child 可跨多个 poll 运行到 action 的绝对期限,证据完成后还有有界的结果宽限期。capacity 未知时 implementation 批次最多两个,但不降低安全 review 的并行度。 | ||
| - 所有 harness 的 native child 等待都必须有界。Codex/GPT 的 `wait_agent`、Claude Task 轮询以及其它原生等待必须在 60 秒内返回;60 秒只限制一次 controller poll,不是 child 的执行上限。每个 live child 都要在 `heartbeatDueAt` 前续租,完成后用 action 给出的 `loop finalize` 原子提交证据和结果,并在每轮 poll 后重新 tick。child 可跨多个 poll 运行到 action 的绝对期限,证据完成后还有有界的结果宽限期。capacity 未知时 implementation 使用默认并发 3,且不降低安全 review 的并行度。harness 能可靠获知当前更大的 child 容量时,可以把它绑定到当前 controller session 并相应提高 `maxParallel`;绝不能猜测或复用过期容量。 | ||
| - 每次成功的有界 controller poll 都会为已认领且仍存活的 child 续短租约,但绝不延长绝对期限。若 poll 只比短租约边界晚一个有界的 60 秒等待窗口,同一个已认领 item 仍可续租;直接提交过期 result 仍会拒绝,真正失联的 item 仍会过期,absolute deadline 永不移动。retryable 下游任务必须先完成缺失的 prerequisite review,再重试 worker。跨任务 finding 只有在额外路径全部属于 task graph 中已完成 owner 时才能自动路由;必须冻结完整 scope,并重新复审发生变化的 owner。只要任何已记录的 cross-task owner 尚未批准,它的 review 或 repair 必须先于新的 implementation 和 retryable worker,同时其它无冲突 reviewer 仍可并行。执行未指定服务的全量 Docker Compose rebuild 前,必须先读取项目发布说明;存在无关服务时改用显式服务名。 | ||
@@ -68,3 +68,3 @@ - 开始或恢复单个 active change 时,用 `ospec execute bootstrap [changes/active/<change>]` 写入带 project session brief snapshot 的 `artifacts/agents/bootstrap.json` 和 `artifacts/agents/bootstrap.md`,然后按其中的下一步安全动作继续 | ||
| - worker 记录 `DONE` 或 `DONE_WITH_CONCERNS` 后,controller-owned Goal 用 `ospec loop tick [changes/active/<change>]` 生成带真实 executor provenance 的合并 review action 和范围受控的 `artifacts/agents/review-packages/*.diff`;只有非 controller 流程才直接运行 `ospec execute review ... --task <task-id>` | ||
| - task graph 启用 `documentation_updates` 后,每个 task 都必须包含该数组(没有则为 `[]`);声明的 docs 路径必须同时出现在同一 task 的 `target_files` 中,并具有从 dispatch 到 complete 的有效内容变化证据。若 evidence 证明已有 baseline 在完成时变为不存在,则经过 review 的删除属于有效变化。多轮 repair 时,finalize 比较首个 baseline 与最终完成态,并要求当前工作区匹配该路径最后一个声明 owner 的 evidence,旧修改或后续回退不能通过。没有历史 baseline 的旧流程仅在声明文档仍存在时保持兼容,并必须把证据标记为无法验证。 | ||
| - task graph 启用 `documentation_updates` 后,每个 task 都必须包含该数组(没有则为 `[]`);声明的 docs 路径必须同时出现在同一 task 的 `target_files` 中,并具有从 dispatch 到 complete 的有效内容变化证据。若 evidence 证明已有 baseline 在完成时变为不存在,则经过 review 的删除属于有效变化。多轮 repair 时,finalize 比较首个 baseline 与最终完成态,并要求当前工作区匹配该路径最后一个声明 owner 的 evidence,旧修改或后续回退不能通过。如果 controller closeout 在最后一次 worker dispatch 后修改了声明路径,只有更晚、executor provenance 有效且 target snapshot 精确匹配当前文件的 APPROVED task review 才能授权最终状态;它不能替代 meaningful-change 证据链。没有历史 baseline 的旧流程仅在声明文档仍存在时保持兼容,并必须把证据标记为无法验证。 | ||
| - 所有单任务 review 通过且 task graph 完成后,controller Loop 用下一次 `ospec loop tick` 生成最终整体 review;只有非 controller 流程才直接运行不带 `--task` 的 `ospec execute review` | ||
@@ -71,0 +71,0 @@ - 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 |
@@ -7,2 +7,2 @@ interface: | ||
| default_prompt: "Use $ospec-change for a small or routine OSpec change. Inspect project state first, read .skillrc and managed for-ai guidance, announce which skill and command you use as you go (Announce-Before-Act) and confirm ambiguous scope, direction, API, UI, or risk with the user one question at a time before implementing (Brainstorm-First), then work inside changes/active/<change>. Create new work with ospec new <change-name> [path]. Keep only proposal.md, tasks.md, state.json, verification.md, and review.md aligned with the actual implementation. Do not create design.md, implementation-plan.md, task graphs, worker packets, or review artifacts unless the user explicitly asks for the full goal workflow. Use ospec-goal for complex, cross-cutting, high-risk, multi-worker, or evidence-heavy work. Verify with ospec verify and close with ospec finalize when ready." | ||
| default_prompt: "Use $ospec-change for an OSpec change selected by the user. Read the compact managed change-protocol.md and stage-relevant change files, announce commands before acting, keep the work on the classic profile without auto-promoting it to Goal, put batch changes in the queue, and maintain proposal.md, tasks.md, state.json, verification.md, and review.md. The current AI performs the lightweight review. Bug fixes may justify no documentation impact; features require real documentation beyond the generated archive summary. Verify and automatically finalize when every closeout gate is ready." |
@@ -23,3 +23,3 @@ --- | ||
| Use `ospec-goal` instead when the work needs design docs, implementation planning, task graph dispatch, parallel workers, document review, evidence artifacts, or full review gates. | ||
| The user owns profile selection. Once the user chooses a change, keep it on the classic flow regardless of complexity, flags, file count, or batch size. Never auto-promote, reject, or replace it with a Goal. Use `ospec-goal` only when the user explicitly selects a Goal. | ||
@@ -29,11 +29,10 @@ ## Read Order | ||
| 1. `.skillrc` | ||
| 2. `.ospec/SKILL.index.json` for nested projects, or root `SKILL.index.json` for legacy classic projects | ||
| 3. `.ospec/for-ai/ai-guide.md` for nested projects, or legacy `for-ai/ai-guide.md` | ||
| 4. `.ospec/for-ai/execution-protocol.md` for nested projects, or legacy `for-ai/execution-protocol.md` | ||
| 5. `.ospec/changes/active/<change>/proposal.md` for nested projects, or legacy `changes/active/<change>/proposal.md` | ||
| 6. `.ospec/changes/active/<change>/tasks.md` for nested projects, or legacy `changes/active/<change>/tasks.md` | ||
| 7. `.ospec/changes/active/<change>/state.json` for nested projects, or legacy `changes/active/<change>/state.json` | ||
| 8. `.ospec/changes/active/<change>/verification.md` for nested projects, or legacy `changes/active/<change>/verification.md` | ||
| 9. `.ospec/changes/active/<change>/review.md` for nested projects, or legacy `changes/active/<change>/review.md` | ||
| 2. Relevant entries from `.ospec/SKILL.index.json` for nested projects, or root `SKILL.index.json` for legacy classic projects | ||
| 3. `.ospec/for-ai/change-protocol.md` for nested projects, or legacy `for-ai/change-protocol.md` | ||
| 4. `.ospec/changes/active/<change>/proposal.md`, `tasks.md`, and `state.json` for nested projects, or their legacy classic paths | ||
| 5. Read `verification.md` only when entering verification | ||
| 6. Read `review.md` only when entering closeout | ||
| If `change-protocol.md` is missing, fall back to the full `ai-guide.md` and `execution-protocol.md`. Read those full guides otherwise only when a blocking plugin is active or one specific ambiguous rule requires them. | ||
| ## Language | ||
@@ -55,10 +54,12 @@ | ||
| 2. If the repo is not initialized, stop at initialization guidance instead of forcing a change. | ||
| 3. If the request is a new requirement, derive a concise kebab-case change name and create it with `ospec new <change-name> [path]`. | ||
| 3. If the request is a new requirement, derive a concise kebab-case change name and create it with `ospec change <change-name> [path]` (`ospec new` remains a compatibility alias). | ||
| 4. If the matching active change already exists, continue it instead of duplicating it. | ||
| 5. Keep the work inside the active change container. | ||
| 6. Keep `proposal.md`, `tasks.md`, `state.json`, `verification.md`, and `review.md` aligned with actual execution. | ||
| 7. Do not create `design.md`, `implementation-plan.md`, task graphs, worker packets, or review artifacts for routine small changes. | ||
| 8. Use `ospec-goal` when the requirement is complex enough to need the full workflow. | ||
| 7. Do not create `design.md`, `implementation-plan.md`, task graphs, worker packets, or Goal review artifacts for changes. | ||
| 8. Put batch changes in the queue and execute them sequentially in a shared worktree. | ||
| 9. Use OSpec closeout commands instead of inventing a parallel process. | ||
| 10. Closeout is automatic when ready: once implementation, `verification.md`, and `review.md` are aligned and `ospec verify [changes/active/<change>]` passes, run `ospec finalize [changes/active/<change>]` yourself. Do not stop at `ospec archive ... --check` (it is a preview only) and do not wait for the user to ask before archiving. **Closeout uses `direct-closeout` (archive locally, no PR) and `manual` merge as defaults — do NOT ask the user about PR, merge, branch, or worktree strategy; uncommitted change/OSpec files in the working tree are normal and do not block archive. Only open a PR if the user explicitly asked.** Only pause closeout when a gate genuinely needs a human: a pending required user decision, an unapproved blocking plugin gate (e.g. Checkpoint), real blockers reported by verify or archive, or an explicit user request to preview or approve before archiving. | ||
| 10. The current AI performs one lightweight `review.md` review. `APPROVED` and `APPROVED_WITH_CONCERNS` may close automatically; `PENDING`, `NEEDS_CHANGES`, and `BLOCKED` stop closeout. | ||
| 11. Set the proposal `change_type` and documentation contract. Bug fixes may record `documentation_impact: none` with a concrete reason. Features and docs changes require at least one real project, module, API, or user document; the generated archive summary does not count. Update `SKILL.md` only when module rules or AI usage contracts changed. Index rebuild is automatic. | ||
| 12. Closeout is automatic when ready: once implementation, `verification.md`, documentation policy, plugin gates, and `review.md` are aligned and `ospec verify [changes/active/<change>]` passes, run `ospec finalize [changes/active/<change>]` yourself. Do not stop at `ospec archive ... --check` (it is a preview only) and do not wait for the user to ask before archiving. **Closeout uses `direct-closeout` (archive locally, no PR) and `manual` merge as defaults — do NOT ask the user about PR, merge, branch, or worktree strategy; uncommitted change/OSpec files in the working tree are normal and do not block archive. Only open a PR if the user explicitly asked.** Only pause closeout when a gate genuinely needs a human: a pending required user decision, an unapproved blocking plugin gate (e.g. Checkpoint), real blockers reported by verify or archive, or an explicit user request to preview or approve before archiving. | ||
@@ -69,3 +70,3 @@ ## Commands | ||
| ospec status [path] | ||
| ospec new <change-name> [path] | ||
| ospec change <change-name> [path] | ||
| ospec changes status [path] | ||
@@ -83,4 +84,4 @@ ospec progress [changes/active/<change>] | ||
| - Do not enter queue mode unless the user explicitly asks for queue behavior. | ||
| - Do not escalate to the full goal workflow unless the work is complex, cross-cutting, high-risk, or explicitly requested as a goal. | ||
| - Never escalate or auto-promote a change to the Goal workflow; only the user selects Goal. | ||
| - Do not claim completion until implementation, verification notes, and closeout status are aligned. | ||
| - If real project tests exist, run or recommend them separately from `ospec verify`. |
@@ -17,2 +17,2 @@ name: ospec-change | ||
| default_prompt: "Use $ospec-change for a small or routine OSpec change. Inspect project state first, read .skillrc and managed for-ai guidance, announce which skill and command you use as you go (Announce-Before-Act) and confirm ambiguous scope, direction, API, UI, or risk with the user one question at a time before implementing (Brainstorm-First), then work inside changes/active/<change>. Create new work with ospec new <change-name> [path]. Keep only proposal.md, tasks.md, state.json, verification.md, and review.md aligned with the actual implementation. Do not create design.md, implementation-plan.md, task graphs, worker packets, or review artifacts unless the user explicitly asks for the full goal workflow. Use ospec-goal for complex, cross-cutting, high-risk, multi-worker, or evidence-heavy work. Verify with ospec verify and close with ospec finalize when ready." | ||
| default_prompt: "Use $ospec-change for an OSpec change selected by the user. Read the compact managed change-protocol.md and stage-relevant change files, announce commands before acting, keep the work on the classic profile without auto-promoting it to Goal, put batch changes in the queue, and maintain proposal.md, tasks.md, state.json, verification.md, and review.md. The current AI performs the lightweight review. Bug fixes may justify no documentation impact; features require real documentation beyond the generated archive summary. Verify and automatically finalize when every closeout gate is ready." |
@@ -22,3 +22,3 @@ --- | ||
| - **`/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`. Unknown native capacity caps implementation batches at two while leaving conflict-safe review batches under the configured limit; a reported capacity replaces that guard. If the session capability expires, OSpec blocks instead of starting an agent CLI. | ||
| - **Scheduling is session-bound.** Controller mode re-runs `loop run --once` and consumes the emitted action batch through `runtimeAdapter.selected.nativeSubagent`. Unknown native capacity uses the default implementation concurrency of three while leaving conflict-safe review batches under the configured limit. When the current harness can authoritatively report a larger positive child capacity, bind it to the active controller session and raise `maxParallel` as appropriate; reported capacities such as 5-10 replace the fallback but never override dependencies, file conflicts, token funding, or the configured maximum. Never guess capacity from a provider name or stale session. If the 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. | ||
@@ -84,3 +84,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. | ||
| 6. Draft or update `implementation-plan.md` from `design.md`; identify target files, expected results, verification commands, dependencies, parallelizable work, and conflicts. | ||
| 7. Derive `artifacts/agents/task-graph.json` from `implementation-plan.md`; give every task a `documentation_updates` array (`[]` when none), include every declared docs path in the same task's `target_files`, and require meaningful-change evidence from dispatch to completion; derive `tasks.md` from the task graph. Reviewed deletion is valid when completion evidence proves an existing baseline became missing. Across repair attempts, finalize compares the first baseline with the final completed state and requires the workspace to match the latest declared-owner evidence; do not restore a legitimately deleted document or rewrite history to satisfy archive. Mark every dependency/file-safe task `parallelizable: true`; every generated `parallelizable: false` task must include `serial_reason`, and `maxParallel=1` must include `maxParallelReason`. Split tasks with more than six `target_files`; when one atomic verification boundary genuinely requires that breadth, record a concrete `scope_reason`. Split implementation/automatic checks from external device, credential, third-party, or manual acceptance so an unavailable external gate cannot block unrelated implementation; downstream work may depend on the implementation slice, while the external acceptance remains a final hard gate. For L3 use `ospec loop allowlist derive/check/apply --from-task-graph` so exact paths and commands fail before expensive document reviewers run; never assume repeated configure calls append. Finalize also generates one indexed `docs/project/changes/<archive-path>.md` for this goal; its preflight refuses to overwrite a human-owned file at that path, and the generated summary does not replace required architecture, API, module, or operational documentation. | ||
| 7. Derive `artifacts/agents/task-graph.json` from `implementation-plan.md`; give every task a `documentation_updates` array (`[]` when none), include every declared docs path in the same task's `target_files`, and require meaningful-change evidence from dispatch to completion; derive `tasks.md` from the task graph. Reviewed deletion is valid when completion evidence proves an existing baseline became missing. Across repair attempts, finalize compares the first baseline with the final completed state and requires the workspace to match the latest declared-owner evidence; do not restore a legitimately deleted document or rewrite history to satisfy archive. If controller closeout changes a declared path after the last worker dispatch, a later APPROVED task review may authorize the final state only when its executor provenance is valid and its target snapshot exactly matches the current path; it never replaces the meaningful-change chain. Run `ospec execute sync` after closeout so localized worker-status sections and checklists derive from authoritative review and verification state instead of manual edits. Mark every dependency/file-safe task `parallelizable: true`; every generated `parallelizable: false` task must include `serial_reason`, and `maxParallel=1` must include `maxParallelReason`. Split tasks with more than six `target_files`; when one atomic verification boundary genuinely requires that breadth, record a concrete `scope_reason`. Split implementation/automatic checks from external device, credential, third-party, or manual acceptance so an unavailable external gate cannot block unrelated implementation; downstream work may depend on the implementation slice, while the external acceptance remains a final hard gate. For L3 use `ospec loop allowlist derive/check/apply --from-task-graph` so exact paths and commands fail before expensive document reviewers run; never assume repeated configure calls append. Finalize also generates one indexed `docs/project/changes/<archive-path>.md` for this goal; its preflight refuses to overwrite a human-owned file at that path, and the generated summary does not replace required architecture, API, module, or operational documentation. | ||
| 8. Run `ospec execute doc-review [changes/active/<goal>] --stage design`. When it reports `Reused approval: yes`, do not launch another reviewer. Otherwise execute one fresh independent design reviewer through `dispatch.runtimeAdapter`, immediately claim its real executor id, wait for Markdown plus structured findings, then complete that executor. Approval must validate before plan review. | ||
@@ -87,0 +87,0 @@ 9. Run `ospec execute doc-review [changes/active/<goal>] --stage plan`. Reuse a valid approval; otherwise execute one fresh independent plan reviewer through `dispatch.runtimeAdapter`, claim its real executor id, wait for findings, then complete it before worker dispatch. The controlling AI must not self-approve either review. |
+13
-8
@@ -65,3 +65,3 @@ #!/usr/bin/env node | ||
| const services_1 = require("./services"); | ||
| const CLI_VERSION = '1.8.15'; | ||
| const CLI_VERSION = '1.8.17'; | ||
| function showInitUsage() { | ||
@@ -159,3 +159,3 @@ console.log('Usage: ospec init [root-dir] [--summary "..."] [--tech-stack node,react] [--architecture "..."] [--document-language en-US|zh-CN|ja-JP|ar]'); | ||
| ? '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]'; | ||
| : `Usage: ospec ${commandName} <change-name> [root-dir] [--flags flag1,flag2]`; | ||
| } | ||
@@ -321,11 +321,15 @@ function parseNewCommandArgs(commandArgs, commandName = 'new') { | ||
| } | ||
| case 'change': | ||
| case 'new': { | ||
| if (commandArgs.length === 0) { | ||
| console.error('Error: change name is required'); | ||
| console.log('Usage: ospec new <change-name> [root-dir] [--flags flag1,flag2]'); | ||
| console.log(getNewLikeUsage(command)); | ||
| process.exit(1); | ||
| } | ||
| const { featureName, rootDir, flags } = parseNewCommandArgs(commandArgs, 'new'); | ||
| const { featureName, rootDir, flags } = parseNewCommandArgs(commandArgs, command); | ||
| const newCmd = new NewCommand_1.NewCommand(); | ||
| await newCmd.execute(featureName, rootDir, { flags }); | ||
| await newCmd.execute(featureName, rootDir, { | ||
| flags, | ||
| workflowProfile: 'change', | ||
| }); | ||
| break; | ||
@@ -503,3 +507,4 @@ } | ||
| init [root-dir] Initialize OSpec to a change-ready state | ||
| new <change-name> [root] Create a new change (supports --flags) | ||
| change <name> [root] Create a classic fast-flow change (supports --flags) | ||
| new <change-name> [root] Backward-compatible alias for ospec change | ||
| goal <goal-name> [root] Create a full OSpec goal (supports --flags) | ||
@@ -535,4 +540,4 @@ brainstorm [path] Write an optional pre-change brainstorm artifact | ||
| ospec init . --summary "Internal admin portal" --tech-stack node,react,postgres | ||
| ospec new onboarding-flow | ||
| ospec new landing-refresh . --flags ui_change,page_design | ||
| ospec change onboarding-flow | ||
| ospec change landing-refresh . --flags ui_change,page_design | ||
| ospec goal billing-refactor . --flags complex_feature,multi_file_change | ||
@@ -539,0 +544,0 @@ ospec goal billing-refactor . --level L2 --target codex --execution-model controller --harness-interactive true --native-subagents supported |
@@ -76,5 +76,11 @@ "use strict"; | ||
| } | ||
| const featureState = await services_1.services.fileService.readJSON(statePath); | ||
| let featureState = await services_1.services.fileService.readJSON(statePath); | ||
| const workflowProfile = await (0, WorkflowProfile_1.inferWorkflowProfileFromChangeDir)(targetPath, featureState); | ||
| const isGoalWorkflow = workflowProfile === WorkflowProfile_1.GOAL_WORKFLOW_PROFILE; | ||
| const classicStatus = !isGoalWorkflow | ||
| ? await services_1.services.projectService.getActiveChangeStatusItem(targetPath) | ||
| : null; | ||
| if (classicStatus?.closeoutState) { | ||
| featureState = classicStatus.closeoutState; | ||
| } | ||
| if (isGoalWorkflow) { | ||
@@ -99,3 +105,3 @@ const progressProjection = await services_1.services.taskGraphExecutionService.reconcileGoalProgress(targetPath); | ||
| : []; | ||
| const archiveConfig = config.workflow?.archive_gate || { | ||
| const configuredArchiveGate = config.workflow?.archive_gate || { | ||
| require_verification: true, | ||
@@ -106,2 +112,9 @@ require_skill_update: true, | ||
| }; | ||
| const archiveConfig = isGoalWorkflow | ||
| ? configuredArchiveGate | ||
| : { | ||
| ...configuredArchiveGate, | ||
| require_skill_update: false, | ||
| require_index_regenerated: false, | ||
| }; | ||
| const result = await ArchiveGate_1.archiveGate.checkArchiveReadiness(featureState, archiveConfig, { | ||
@@ -115,2 +128,19 @@ activatedSteps, | ||
| }); | ||
| if (classicStatus) { | ||
| for (const check of classicStatus.checks) { | ||
| if (check.name === 'archive.pending') | ||
| continue; | ||
| if (!result.checks.some(item => item.name === check.name)) { | ||
| result.checks.push({ | ||
| name: check.name, | ||
| passed: check.status !== 'fail', | ||
| message: check.message, | ||
| }); | ||
| } | ||
| if (check.status === 'fail') | ||
| result.blockers.push(check.message); | ||
| if (check.status === 'warn') | ||
| result.warnings.push(check.message); | ||
| } | ||
| } | ||
| if (isGoalWorkflow) { | ||
@@ -236,11 +266,13 @@ const reviewArtifactSet = await (0, ReviewArtifacts_1.resolveGoalReviewArtifacts)(services_1.services.fileService, targetPath); | ||
| } | ||
| const documentationUpdateAnalysis = await services_1.services.projectService.analyzeDocumentationUpdates(targetPath); | ||
| for (const check of documentationUpdateAnalysis.checks) { | ||
| result.checks.push({ | ||
| name: check.name, | ||
| passed: check.status !== 'fail', | ||
| message: check.message, | ||
| }); | ||
| if (check.status === 'fail') { | ||
| result.blockers.push(check.message); | ||
| if (isGoalWorkflow) { | ||
| const documentationUpdateAnalysis = await services_1.services.projectService.analyzeDocumentationUpdates(targetPath); | ||
| for (const check of documentationUpdateAnalysis.checks) { | ||
| result.checks.push({ | ||
| name: check.name, | ||
| passed: check.status !== 'fail', | ||
| message: check.message, | ||
| }); | ||
| if (check.status === 'fail') { | ||
| result.blockers.push(check.message); | ||
| } | ||
| } | ||
@@ -350,2 +382,4 @@ } | ||
| } | ||
| result.blockers.splice(0, result.blockers.length, ...Array.from(new Set(result.blockers))); | ||
| result.warnings.splice(0, result.warnings.length, ...Array.from(new Set(result.warnings))); | ||
| result.canArchive = result.blockers.length === 0; | ||
@@ -379,3 +413,2 @@ console.log('\nArchive Gate Check:'); | ||
| } | ||
| await services_1.services.projectService.rebuildIndex(projectRoot); | ||
| const archivePath = await this.performArchive(targetPath, projectRoot, featureState, config); | ||
@@ -382,0 +415,0 @@ this.success(`Change archived to ${archivePath}`); |
@@ -7,3 +7,2 @@ "use strict"; | ||
| const BaseCommand_1 = require("./BaseCommand"); | ||
| const VerifyCommand_1 = require("./VerifyCommand"); | ||
| class FinalizeCommand extends BaseCommand_1.BaseCommand { | ||
@@ -14,4 +13,2 @@ async execute(featurePath) { | ||
| this.info(`Finalizing change at ${targetPath}`); | ||
| const verifyCmd = new VerifyCommand_1.VerifyCommand(); | ||
| await verifyCmd.execute(targetPath); | ||
| const result = await services_1.services.projectService.finalizeChange(path.resolve(targetPath)); | ||
@@ -18,0 +15,0 @@ this.success(`Change finalized: ${result.archivePath}`); |
@@ -98,2 +98,13 @@ "use strict"; | ||
| const documentLanguage = await this.resolveDocumentLanguage(targetDir, config); | ||
| const templateContext = { | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| workflowProfile, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| }; | ||
| await services_1.services.fileService.writeJSON(path.join(featureDir, constants_1.FILE_NAMES.STATE), services_1.services.stateManager.createInitialState(featureName, [], config.mode, placement === constants_1.DIR_NAMES.QUEUED | ||
@@ -109,10 +120,3 @@ ? { | ||
| await services_1.services.fileService.writeFile(path.join(featureDir, constants_1.FILE_NAMES.PROPOSAL), services_1.services.templateEngine.generateProposalTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(featureDir, constants_1.FILE_NAMES.PROPOSAL), | ||
@@ -122,21 +126,7 @@ })); | ||
| await services_1.services.fileService.writeFile(path.join(featureDir, constants_1.FILE_NAMES.DESIGN), services_1.services.templateEngine.generateDesignTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(featureDir, constants_1.FILE_NAMES.DESIGN), | ||
| })); | ||
| await services_1.services.fileService.writeFile(path.join(featureDir, constants_1.FILE_NAMES.IMPLEMENTATION_PLAN), services_1.services.templateEngine.generateImplementationPlanTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(featureDir, constants_1.FILE_NAMES.IMPLEMENTATION_PLAN), | ||
@@ -146,21 +136,7 @@ })); | ||
| await services_1.services.fileService.writeFile(path.join(featureDir, constants_1.FILE_NAMES.TASKS), services_1.services.templateEngine.generateTasksTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(featureDir, constants_1.FILE_NAMES.TASKS), | ||
| })); | ||
| await services_1.services.fileService.writeFile(path.join(featureDir, constants_1.FILE_NAMES.VERIFICATION), services_1.services.templateEngine.generateVerificationTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(featureDir, constants_1.FILE_NAMES.VERIFICATION), | ||
@@ -172,21 +148,7 @@ })); | ||
| await services_1.services.fileService.writeFile(path.join(agentArtifactsDir, constants_1.FILE_NAMES.TASK_GRAPH), services_1.services.templateEngine.generateTaskGraphTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(agentArtifactsDir, constants_1.FILE_NAMES.TASK_GRAPH), | ||
| })); | ||
| await services_1.services.fileService.writeFile(path.join(agentArtifactsDir, constants_1.FILE_NAMES.AGENT_WORKER_STATUS), services_1.services.templateEngine.generateAgentWorkerStatusTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(agentArtifactsDir, constants_1.FILE_NAMES.AGENT_WORKER_STATUS), | ||
@@ -196,10 +158,3 @@ })); | ||
| await services_1.services.fileService.writeFile(path.join(featureDir, constants_1.FILE_NAMES.REVIEW), services_1.services.templateEngine.generateReviewTemplate({ | ||
| feature: featureName, | ||
| mode: config.mode, | ||
| placement, | ||
| projectContext, | ||
| flags, | ||
| optionalSteps: activatedSteps, | ||
| documentLanguage, | ||
| projectRoot: targetDir, | ||
| ...templateContext, | ||
| documentPath: path.join(featureDir, constants_1.FILE_NAMES.REVIEW), | ||
@@ -206,0 +161,0 @@ })); |
@@ -78,2 +78,3 @@ "use strict"; | ||
| source: 'queue', | ||
| workflowProfile: 'change', | ||
| }); | ||
@@ -80,0 +81,0 @@ } |
@@ -366,2 +366,3 @@ export type ProjectMode = 'lite' | 'standard' | 'full'; | ||
| checks: ChangeStatusCheck[]; | ||
| closeoutState?: FeatureState; | ||
| } | ||
@@ -368,0 +369,0 @@ export interface ActiveChangeStatusReport { |
@@ -32,2 +32,15 @@ "use strict"; | ||
| { | ||
| id: 'change-protocol', | ||
| category: 'ai_guidance', | ||
| description: 'Compact stage-aware protocol for classic change execution.', | ||
| targetRelativePath: 'for-ai/change-protocol.md', | ||
| overwritePolicy: 'if_missing', | ||
| localizedSources: { | ||
| 'zh-CN': 'assets/for-ai/zh-CN/change-protocol.md', | ||
| 'en-US': 'assets/for-ai/en-US/change-protocol.md', | ||
| 'ja-JP': 'assets/for-ai/ja-JP/change-protocol.md', | ||
| 'ar': 'assets/for-ai/ar/change-protocol.md', | ||
| }, | ||
| }, | ||
| { | ||
| id: 'build-index-script', | ||
@@ -34,0 +47,0 @@ category: 'tooling', |
@@ -75,2 +75,14 @@ "use strict"; | ||
| const now = new Date().toISOString(); | ||
| const workflowProfileId = options.workflowProfileId ?? 'change'; | ||
| const pending = [ | ||
| 'proposal_complete', | ||
| 'tasks_complete', | ||
| 'implementation_complete', | ||
| ...(workflowProfileId === 'goal' | ||
| ? ['skill_updated', 'index_regenerated'] | ||
| : []), | ||
| 'tests_passed', | ||
| 'verification_passed', | ||
| 'archived', | ||
| ]; | ||
| if (options.queued) { | ||
@@ -81,3 +93,3 @@ return { | ||
| mode, | ||
| workflow_profile_id: options.workflowProfileId ?? 'change', | ||
| workflow_profile_id: workflowProfileId, | ||
| status: 'queued', | ||
@@ -87,12 +99,3 @@ current_step: 'queued', | ||
| completed: [], | ||
| pending: [ | ||
| 'proposal_complete', | ||
| 'tasks_complete', | ||
| 'implementation_complete', | ||
| 'skill_updated', | ||
| 'index_regenerated', | ||
| 'tests_passed', | ||
| 'verification_passed', | ||
| 'archived', | ||
| ], | ||
| pending: [...pending], | ||
| blocked_by: ['awaiting_activation'], | ||
@@ -108,3 +111,3 @@ queued_at: now, | ||
| mode, | ||
| workflow_profile_id: options.workflowProfileId ?? 'change', | ||
| workflow_profile_id: workflowProfileId, | ||
| status: 'draft', | ||
@@ -114,12 +117,3 @@ current_step: 'write_proposal', | ||
| completed: [], | ||
| pending: [ | ||
| 'proposal_complete', | ||
| 'tasks_complete', | ||
| 'implementation_complete', | ||
| 'skill_updated', | ||
| 'index_regenerated', | ||
| 'tests_passed', | ||
| 'verification_passed', | ||
| 'archived', | ||
| ], | ||
| pending, | ||
| blocked_by: ['missing_proposal'], | ||
@@ -126,0 +120,0 @@ last_updated: now, |
@@ -160,2 +160,10 @@ "use strict"; | ||
| flags: context.flags, | ||
| ...(context.workflowProfile === 'change' | ||
| ? { | ||
| change_type: 'pending', | ||
| documentation_impact: 'pending', | ||
| documentation_updates: [], | ||
| documentation_reason: '', | ||
| } | ||
| : {}), | ||
| }, this.copy(context.documentLanguage, zh, en, ja, ar)); | ||
@@ -435,2 +443,30 @@ } | ||
| ${optionalStepTasksAr}`.trim(); | ||
| const changeZh = `## 快速任务清单 | ||
| - [ ] 完成本次 change 的实现 | ||
| - [ ] 运行与改动相关的测试或检查,并记录结果 | ||
| - [ ] 按 proposal 中的 \`documentation_impact\` 同步真实文档,或确认无需更新的原因 | ||
| - [ ] 更新 \`verification.md\` | ||
| - [ ] 完成当前 AI 的轻量 \`review.md\``; | ||
| const changeEn = `## Fast Task Checklist | ||
| - [ ] Implement this change | ||
| - [ ] Run checks or tests relevant to the change and record the results | ||
| - [ ] Follow the proposal \`documentation_impact\` contract by updating real documentation or confirming why none is needed | ||
| - [ ] Update \`verification.md\` | ||
| - [ ] Complete the current AI's lightweight \`review.md\``; | ||
| const changeJa = `## 高速タスクチェックリスト | ||
| - [ ] この change を実装する | ||
| - [ ] change に関連するテストまたはチェックを実行して結果を記録する | ||
| - [ ] proposal の \`documentation_impact\` に従って実文書を更新するか、更新不要の理由を確認する | ||
| - [ ] \`verification.md\` を更新する | ||
| - [ ] 現在の AI による軽量な \`review.md\` を完了する`; | ||
| const changeAr = `## قائمة المهام السريعة | ||
| - [ ] نفّذ هذا change | ||
| - [ ] شغّل الاختبارات أو الفحوص ذات الصلة وسجّل النتائج | ||
| - [ ] اتبع عقد \`documentation_impact\` في proposal بتحديث وثائق حقيقية أو تأكيد سبب عدم الحاجة | ||
| - [ ] حدّث \`verification.md\` | ||
| - [ ] أكمل \`review.md\` الخفيفة بواسطة AI الحالي`; | ||
| return this.withFrontmatter({ | ||
@@ -440,3 +476,5 @@ feature: context.feature, | ||
| optional_steps: context.optionalSteps, | ||
| }, this.copy(context.documentLanguage, zh, en, ja, ar)); | ||
| }, context.workflowProfile === 'change' | ||
| ? this.copy(context.documentLanguage, changeZh, changeEn, changeJa, changeAr) | ||
| : this.copy(context.documentLanguage, zh, en, ja, ar)); | ||
| } | ||
@@ -744,2 +782,46 @@ finally { | ||
| - [ ] جاهز للأرشفة`; | ||
| const changeZh = `## 相关验证 | ||
| - [ ] 已记录实际运行的测试或检查命令及其结果 | ||
| - [ ] 验收标准已逐项确认 | ||
| - [ ] proposal 声明的真实文档更新已完成,或已确认 \`documentation_impact: none\` | ||
| - [ ] 没有未解决的阻塞问题 | ||
| - [ ] 可以归档 | ||
| ## 命令与结果 | ||
| - 待补充`; | ||
| const changeEn = `## Relevant Verification | ||
| - [ ] Actual test or check commands and their results are recorded | ||
| - [ ] Acceptance criteria are confirmed | ||
| - [ ] Real documentation updates declared by the proposal are complete, or \`documentation_impact: none\` is confirmed | ||
| - [ ] No blocking issue remains | ||
| - [ ] Ready to archive | ||
| ## Commands And Results | ||
| - TBD`; | ||
| const changeJa = `## 関連する検証 | ||
| - [ ] 実行したテストまたはチェックコマンドと結果を記録した | ||
| - [ ] 受け入れ条件を確認した | ||
| - [ ] proposal で宣言した実文書の更新を完了した、または \`documentation_impact: none\` を確認した | ||
| - [ ] 未解決の blocker がない | ||
| - [ ] archive 可能 | ||
| ## コマンドと結果 | ||
| - 未定`; | ||
| const changeAr = `## التحقق ذو الصلة | ||
| - [ ] سُجلت أوامر الاختبار أو الفحص الفعلية ونتائجها | ||
| - [ ] تم تأكيد معايير القبول | ||
| - [ ] اكتملت تحديثات الوثائق الحقيقية المعلنة في proposal أو تم تأكيد \`documentation_impact: none\` | ||
| - [ ] لا توجد مشكلة حاجبة غير محلولة | ||
| - [ ] جاهز للأرشفة | ||
| ## الأوامر والنتائج | ||
| - قيد التحديد`; | ||
| return this.withFrontmatter({ | ||
@@ -751,3 +833,5 @@ feature: context.feature, | ||
| passed_optional_steps: [], | ||
| }, this.copy(context.documentLanguage, zh, en, ja, ar)); | ||
| }, context.workflowProfile === 'change' | ||
| ? this.copy(context.documentLanguage, changeZh, changeEn, changeJa, changeAr) | ||
| : this.copy(context.documentLanguage, zh, en, ja, ar)); | ||
| } | ||
@@ -1400,2 +1484,46 @@ finally { | ||
| - [ ] جاهز للانتقال إلى التحقق / الأرشفة`; | ||
| const changeZh = `## 轻量 Review | ||
| - [ ] 实现符合 proposal 的目标、范围和验收标准 | ||
| - [ ] 已检查相关测试结果和主要回归风险 | ||
| - [ ] 已核对 \`documentation_impact\` 与实际文档更新 | ||
| - [ ] 已记录 concern、未决问题或确认没有发现 | ||
| - [ ] 已把最终判定写入 frontmatter 的 \`decision\` | ||
| ## Findings | ||
| - 未发现;如有问题在此记录`; | ||
| const changeEn = `## Lightweight Review | ||
| - [ ] Implementation matches the proposal goals, scope, and acceptance criteria | ||
| - [ ] Relevant test results and primary regression risks are checked | ||
| - [ ] \`documentation_impact\` matches the actual documentation updates | ||
| - [ ] Concerns or open issues are recorded, or their absence is confirmed | ||
| - [ ] The final decision is written to the frontmatter \`decision\` | ||
| ## Findings | ||
| - None found; record any issue here`; | ||
| const changeJa = `## 軽量 Review | ||
| - [ ] 実装が proposal の目標、範囲、受け入れ条件に一致する | ||
| - [ ] 関連するテスト結果と主要な回帰リスクを確認した | ||
| - [ ] \`documentation_impact\` が実際の文書更新と一致する | ||
| - [ ] concern や未解決事項を記録した、または存在しないことを確認した | ||
| - [ ] 最終判定を frontmatter の \`decision\` に記録した | ||
| ## Findings | ||
| - なし。問題があればここに記録する`; | ||
| const changeAr = `## مراجعة خفيفة | ||
| - [ ] يطابق التنفيذ أهداف proposal ونطاقها ومعايير القبول | ||
| - [ ] تمت مراجعة نتائج الاختبارات ذات الصلة ومخاطر التراجع الرئيسية | ||
| - [ ] يطابق \`documentation_impact\` تحديثات الوثائق الفعلية | ||
| - [ ] سُجلت concerns أو المسائل المفتوحة أو تم تأكيد عدم وجودها | ||
| - [ ] كُتب القرار النهائي في frontmatter الحقل \`decision\` | ||
| ## Findings | ||
| - لا توجد؛ سجّل أي مشكلة هنا`; | ||
| return this.withFrontmatter({ | ||
@@ -1405,3 +1533,11 @@ feature: context.feature, | ||
| status: 'pending_review', | ||
| }, this.copy(context.documentLanguage, zh, en, ja, ar)); | ||
| ...(context.workflowProfile === 'change' | ||
| ? { | ||
| reviewer_mode: 'current_ai', | ||
| decision: 'PENDING', | ||
| } | ||
| : {}), | ||
| }, context.workflowProfile === 'change' | ||
| ? this.copy(context.documentLanguage, changeZh, changeEn, changeJa, changeAr) | ||
| : this.copy(context.documentLanguage, zh, en, ja, ar)); | ||
| } | ||
@@ -1408,0 +1544,0 @@ finally { |
@@ -13,2 +13,3 @@ "use strict"; | ||
| placement: 'active', | ||
| workflowProfile: 'change', | ||
| affects: [], | ||
@@ -36,2 +37,3 @@ flags: [], | ||
| : constants_1.DIR_NAMES.ACTIVE, | ||
| workflowProfile: input.workflowProfile === 'goal' ? 'goal' : 'change', | ||
| affects: input.affects ?? [], | ||
@@ -38,0 +40,0 @@ flags: input.flags ?? [], |
@@ -1,2 +0,2 @@ | ||
| import { ProjectMode } from '../../core/types'; | ||
| import { ProjectMode, WorkflowProfileId } from '../../core/types'; | ||
| import { ProjectPresetId } from '../../presets/ProjectPresets'; | ||
@@ -8,2 +8,3 @@ export type TemplateDocumentLanguage = 'zh-CN' | 'en-US' | 'ja-JP' | 'ar'; | ||
| placement?: 'active' | 'queued'; | ||
| workflowProfile?: WorkflowProfileId; | ||
| affects?: string[]; | ||
@@ -26,2 +27,3 @@ flags?: string[]; | ||
| placement: 'active' | 'queued'; | ||
| workflowProfile: WorkflowProfileId; | ||
| affects: string[]; | ||
@@ -28,0 +30,0 @@ flags: string[]; |
@@ -0,1 +1,2 @@ | ||
| import type { ActiveChangeStatusItem } from '../core/types'; | ||
| export interface VerificationCheck { | ||
@@ -13,2 +14,3 @@ name: string; | ||
| workflowProfile: string; | ||
| changeStatus?: ActiveChangeStatusItem; | ||
| } | ||
@@ -15,0 +17,0 @@ /** |
@@ -82,2 +82,30 @@ "use strict"; | ||
| const isGoalWorkflow = workflowProfile === WorkflowProfile_1.GOAL_WORKFLOW_PROFILE; | ||
| if (!isGoalWorkflow) { | ||
| const changeStatus = await services.projectService.getActiveChangeStatusItem(targetPath); | ||
| const checks = [...changeStatus.checks]; | ||
| if (!changeStatus.archiveReady && !checks.some(check => check.status === 'fail')) { | ||
| checks.push({ | ||
| name: 'archive.readiness', | ||
| status: 'fail', | ||
| message: 'Classic change closeout is not ready to archive', | ||
| }); | ||
| } | ||
| const failCount = checks.filter(check => check.status === 'fail').length; | ||
| const warnCount = checks.filter(check => check.status === 'warn').length; | ||
| const passed = failCount === 0 && changeStatus.archiveReady; | ||
| const summary = failCount > 0 | ||
| ? `${failCount} verification(s) failed` | ||
| : warnCount > 0 | ||
| ? `${warnCount} warning(s) found` | ||
| : 'All verifications passed'; | ||
| return { | ||
| passed, | ||
| checks, | ||
| summary, | ||
| failCount, | ||
| warnCount, | ||
| workflowProfile, | ||
| changeStatus, | ||
| }; | ||
| } | ||
| const progressProjection = isGoalWorkflow && taskGraphExists | ||
@@ -84,0 +112,0 @@ ? await services.taskGraphExecutionService.reconcileGoalProgress(targetPath) |
+1
-1
| { | ||
| "name": "@clawplays/ospec-cli", | ||
| "version": "1.8.15", | ||
| "version": "1.8.17", | ||
| "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", |
+7
-7
@@ -95,3 +95,3 @@ <h1><a href="https://ospec.ai/" target="_blank" rel="noopener noreferrer">OSpec.ai</a></h1> | ||
| - CLI language resolution order: explicit `--document-language` -> persisted project language in `.skillrc` -> existing project docs / managed `for-ai/*` guidance / asset manifest -> fallback `en-US` | ||
| - OSpec persists the chosen project document language in `.skillrc` and reuses it for `for-ai` guidance, `ospec new`, and `ospec update` | ||
| - OSpec persists the chosen project document language in `.skillrc` and reuses it for `for-ai` guidance, `ospec change`, and `ospec update` | ||
| - new projects initialized by `ospec init` default to the nested layout: root `.skillrc` and `README.md`, with OSpec-managed files under `.ospec/` | ||
@@ -126,5 +126,5 @@ - plain init does not create optional knowledge maps such as `.ospec/knowledge/src/` or `.ospec/knowledge/tests/`; those appear only when a project already has legacy knowledge content to migrate or when future explicit knowledge-generation flows create them | ||
| ```bash | ||
| ospec new docs-homepage-refresh . | ||
| ospec new fix-login-timeout . | ||
| ospec new update-billing-copy . | ||
| ospec change docs-homepage-refresh . | ||
| ospec change fix-login-timeout . | ||
| ospec change update-billing-copy . | ||
| ``` | ||
@@ -190,3 +190,3 @@ | ||
| Use a goal when the work touches several parts of the project, has important design choices, changes an API or data, carries security or migration risk, or will take several rounds to finish. For a small, well-defined edit, use `ospec new` instead. | ||
| Use a Goal only when you choose the full workflow. A user-selected Change remains a Change regardless of complexity, file count, risk, or batch size; create it with `ospec change` (`ospec new` remains an alias). | ||
@@ -277,3 +277,3 @@ Start from a terminal: | ||
| │ 3. EXECUTION │ | ||
| │ ospec new <change-name> # classic fast change │ | ||
| │ ospec change <change-name> # classic fast change │ | ||
| │ ospec goal <goal-name> # full goal workflow │ | ||
@@ -328,3 +328,3 @@ │ ospec brainstorm / plan (optional pre-change aids) │ | ||
| - **Goal experience contracts**: every goal runs with `Announce-Before-Act` (the AI announces its skill and stage, the `ospec execute …` command and the artifact it writes, and each subagent dispatch), `Brainstorm-First` (open direction, architecture, API, UI, risk, and scope decisions are asked one at a time through the native question UI before design is locked), and `Zero-Setup` (you only start a goal and describe the requirement — the AI runs every `ospec` command itself). In Claude Code, `ospec session hook --target claude --apply` adds hooks that announce every dispatch and hard-block subagent dispatch while a required decision is still pending. | ||
| - **Optional pre-change aids**: `ospec brainstorm` writes durable exploration artifacts under `.ospec/brainstorms/`, with an optional static visual companion; `ospec plan` writes plan drafts under `.ospec/plans/` and only updates `implementation-plan.md` when `--apply` is passed. The default small-change flow starts with `ospec new`; the full workflow starts with `ospec goal`. | ||
| - **Optional pre-change aids**: `ospec brainstorm` writes durable exploration artifacts under `.ospec/brainstorms/`, with an optional static visual companion; `ospec plan` writes plan drafts under `.ospec/plans/` and only updates `implementation-plan.md` when `--apply` is passed. The Change flow starts with `ospec change` (`ospec new` is an alias); the Goal flow starts with `ospec goal` only when selected by the user. | ||
| - **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`. | ||
@@ -331,0 +331,0 @@ - **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. |
+4
-4
@@ -21,5 +21,5 @@ --- | ||
| - Use `ospec new` / `ospec-change` for routine, scoped work. Its source of truth is `changes/active/<change>/proposal.md`, `changes/active/<change>/tasks.md`, `state.json`, `verification.md`, and `review.md`. | ||
| - Use `ospec goal` / `ospec-goal` for complex, cross-cutting, high-risk, parallel, or evidence-heavy work. It additionally owns `changes/active/<change>/design.md`, `changes/active/<change>/implementation-plan.md`, `changes/active/<change>/artifacts/agents/task-graph.json`, worker/reviewer artifacts, and evidence gates. | ||
| - Do not silently promote a change to a goal or create goal-only artifacts for a classic change. A routing recommendation may explain why one workflow fits, but the user's explicit choice remains authoritative. | ||
| - Use `ospec change` / `ospec-change` when the user selects a Change. Its source of truth is `changes/active/<change>/proposal.md`, `changes/active/<change>/tasks.md`, `state.json`, `verification.md`, and `review.md`; `ospec new` remains a compatibility alias. | ||
| - Use `ospec goal` / `ospec-goal` only when the user selects a Goal. It additionally owns `changes/active/<change>/design.md`, `changes/active/<change>/implementation-plan.md`, `changes/active/<change>/artifacts/agents/task-graph.json`, worker/reviewer artifacts, and evidence gates. | ||
| - Never auto-promote, reject, or replace a user-selected Change because of complexity, risk, file count, parallelism, or batch size. The user's explicit profile choice is authoritative. | ||
| - Enter queue mode only when the user explicitly asks to queue or execute multiple changes. | ||
@@ -81,3 +81,3 @@ | ||
| Initialize: ospec init [path] -> verify generated project shell | ||
| Change: ospec new <name> -> implement -> ospec verify -> ospec finalize | ||
| Change: ospec change <name> -> implement -> ospec verify -> ospec finalize | ||
| Goal: ospec goal <name> -> session/bootstrap -> design/plan reviews -> dispatch/review -> verify/finalize | ||
@@ -84,0 +84,0 @@ Docs: ospec docs generate [path] -> ospec docs status -> ospec index check |
+4
-1
@@ -23,2 +23,3 @@ name: ospec | ||
| - "for-ai/ai-guide.md" | ||
| - "for-ai/change-protocol.md" | ||
| - "for-ai/execution-protocol.md" | ||
@@ -34,2 +35,3 @@ - "for-ai/naming-conventions.md" | ||
| - "changes/active/<feature>/verification.md" | ||
| - "changes/active/<feature>/review.md" | ||
| feature_name_format: "kebab-case" | ||
@@ -48,3 +50,3 @@ | ||
| - id: new | ||
| command: "ospec new <feature-name> [root-dir]" | ||
| command: "ospec change <feature-name> [root-dir]" | ||
| purpose: "Create a new change under changes/active with proposal/tasks/state/verification/review." | ||
@@ -104,2 +106,3 @@ - id: changes_status | ||
| - "changes/active/<feature>/verification.md" | ||
| - "changes/active/<feature>/review.md" | ||
| archive_requirements: | ||
@@ -106,0 +109,0 @@ - "state.json.status must be ready_to_archive" |
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.
3372889
1.61%234
2.63%59207
1.33%68
1.49%