diff --git a/.changeset/verify-change-artifact-paths.md b/.changeset/verify-change-artifact-paths.md new file mode 100644 index 0000000000..d5571310d8 --- /dev/null +++ b/.changeset/verify-change-artifact-paths.md @@ -0,0 +1,5 @@ +--- +"@fission-ai/openspec": patch +--- + +The verify workflow now finds a change's spec and design artifacts by their output path (`specs/` and `design.md`) instead of the hardcoded artifact ids `specs` and `design`, so it works with custom schemas whose artifacts use other ids. diff --git a/skills/openspec-verify-change/SKILL.md b/skills/openspec-verify-change/SKILL.md index 41be312432..9cbfb35a25 100644 --- a/skills/openspec-verify-change/SKILL.md +++ b/skills/openspec-verify-change/SKILL.md @@ -90,9 +90,9 @@ In both branches, never create the root as a side effect: do not run `openspec i - Recommendation: "Complete task: " or "Mark as done if already implemented" **Spec Coverage**: - - If status marks the spec artifact skipped by `skip_specs: true`, or the schema defines no spec artifact, report the spec-dependent checks as not applicable. - - Otherwise, `contextFiles` is keyed by artifact id, and artifact ids come from the active schema. If `contextFiles.specs` is absent or empty, mark **Spec Coverage**, **Requirement Implementation Mapping**, and **Scenario Coverage** as not verified; do not treat any of them as clean. - - If delta specs exist in `contextFiles.specs`: + - If status marks the spec artifact skipped by `skip_specs: true`, or the schema defines no spec artifact (no artifact whose `artifactPaths..outputPath` is under `specs/`), report the spec-dependent checks as not applicable. + - Otherwise, `contextFiles` is keyed by artifact id, and artifact ids come from the active schema, so do not assume an id such as `specs`. The spec artifacts are those whose `artifactPaths..outputPath` is under `specs/`; read their files from `contextFiles.`. If those spec files are absent or empty, mark **Spec Coverage**, **Requirement Implementation Mapping**, and **Scenario Coverage** as not verified; do not treat any of them as clean. + - If delta specs exist in those spec files: - Extract all requirements (marked with "### Requirement:", or listed as `FROM:`/`TO:` pairs under `## RENAMED Requirements`) and note the delta section each one sits under: `## ADDED`, `## MODIFIED`, `## REMOVED`, or `## RENAMED Requirements`. The section decides what the check looks for. - For each ADDED or MODIFIED requirement (for MODIFIED, check the text in the delta, not the old wording): - Search codebase for keywords related to the requirement @@ -141,14 +141,14 @@ In both branches, never create the root as a side effect: do not run `openspec i 7. **Verify Coherence** **Design Adherence**: - - If the schema defines no design artifact, report **Design Adherence** as not applicable. - - If `contextFiles.design` exists: + - If the schema defines no design artifact (no artifact with id `design`, and none whose `artifactPaths..outputPath` is or ends in `design.md`), report **Design Adherence** as not applicable. + - If the design artifact's `contextFiles.` file exists: - Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:") - Verify implementation follows those decisions - If contradiction detected: - Add WARNING: "Design decision not followed: " - Recommendation: "Update implementation or revise design.md to match reality" - - Otherwise, if `contextFiles.design` is absent or empty: mark **Design Adherence** as not verified. With other supporting artifacts, **Code Pattern Consistency** still runs; the task-only case remains limited to task completion. + - Otherwise, if the design artifact's `contextFiles.` file is absent or empty: mark **Design Adherence** as not verified. With other supporting artifacts, **Code Pattern Consistency** still runs; the task-only case remains limited to task completion. **Code Pattern Consistency**: - If implementation changes cannot be identified, mark **Code Pattern Consistency** as not verified and explain the missing evidence. diff --git a/src/core/templates/workflows/verify-change.ts b/src/core/templates/workflows/verify-change.ts index 544e1da608..cd40f7e653 100644 --- a/src/core/templates/workflows/verify-change.ts +++ b/src/core/templates/workflows/verify-change.ts @@ -84,9 +84,9 @@ ${PROJECT_ROOT_GUARD} - Recommendation: "Complete task: " or "Mark as done if already implemented" **Spec Coverage**: - - If status marks the spec artifact skipped by \`skip_specs: true\`, or the schema defines no spec artifact, report the spec-dependent checks as not applicable. - - Otherwise, \`contextFiles\` is keyed by artifact id, and artifact ids come from the active schema. If \`contextFiles.specs\` is absent or empty, mark **Spec Coverage**, **Requirement Implementation Mapping**, and **Scenario Coverage** as not verified; do not treat any of them as clean. - - If delta specs exist in \`contextFiles.specs\`: + - If status marks the spec artifact skipped by \`skip_specs: true\`, or the schema defines no spec artifact (no artifact whose \`artifactPaths..outputPath\` is under \`specs/\`), report the spec-dependent checks as not applicable. + - Otherwise, \`contextFiles\` is keyed by artifact id, and artifact ids come from the active schema, so do not assume an id such as \`specs\`. The spec artifacts are those whose \`artifactPaths..outputPath\` is under \`specs/\`; read their files from \`contextFiles.\`. If those spec files are absent or empty, mark **Spec Coverage**, **Requirement Implementation Mapping**, and **Scenario Coverage** as not verified; do not treat any of them as clean. + - If delta specs exist in those spec files: - Extract all requirements (marked with "### Requirement:", or listed as \`FROM:\`/\`TO:\` pairs under \`## RENAMED Requirements\`) and note the delta section each one sits under: \`## ADDED\`, \`## MODIFIED\`, \`## REMOVED\`, or \`## RENAMED Requirements\`. The section decides what the check looks for. - For each ADDED or MODIFIED requirement (for MODIFIED, check the text in the delta, not the old wording): - Search codebase for keywords related to the requirement @@ -135,14 +135,14 @@ ${PROJECT_ROOT_GUARD} 7. **Verify Coherence** **Design Adherence**: - - If the schema defines no design artifact, report **Design Adherence** as not applicable. - - If \`contextFiles.design\` exists: + - If the schema defines no design artifact (no artifact with id \`design\`, and none whose \`artifactPaths..outputPath\` is or ends in \`design.md\`), report **Design Adherence** as not applicable. + - If the design artifact's \`contextFiles.\` file exists: - Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:") - Verify implementation follows those decisions - If contradiction detected: - Add WARNING: "Design decision not followed: " - Recommendation: "Update implementation or revise design.md to match reality" - - Otherwise, if \`contextFiles.design\` is absent or empty: mark **Design Adherence** as not verified. With other supporting artifacts, **Code Pattern Consistency** still runs; the task-only case remains limited to task completion. + - Otherwise, if the design artifact's \`contextFiles.\` file is absent or empty: mark **Design Adherence** as not verified. With other supporting artifacts, **Code Pattern Consistency** still runs; the task-only case remains limited to task completion. **Code Pattern Consistency**: - If implementation changes cannot be identified, mark **Code Pattern Consistency** as not verified and explain the missing evidence. @@ -294,9 +294,9 @@ ${PROJECT_ROOT_GUARD} - Recommendation: "Complete task: " or "Mark as done if already implemented" **Spec Coverage**: - - If status marks the spec artifact skipped by \`skip_specs: true\`, or the schema defines no spec artifact, report the spec-dependent checks as not applicable. - - Otherwise, \`contextFiles\` is keyed by artifact id, and artifact ids come from the active schema. If \`contextFiles.specs\` is absent or empty, mark **Spec Coverage**, **Requirement Implementation Mapping**, and **Scenario Coverage** as not verified; do not treat any of them as clean. - - If delta specs exist in \`contextFiles.specs\`: + - If status marks the spec artifact skipped by \`skip_specs: true\`, or the schema defines no spec artifact (no artifact whose \`artifactPaths..outputPath\` is under \`specs/\`), report the spec-dependent checks as not applicable. + - Otherwise, \`contextFiles\` is keyed by artifact id, and artifact ids come from the active schema, so do not assume an id such as \`specs\`. The spec artifacts are those whose \`artifactPaths..outputPath\` is under \`specs/\`; read their files from \`contextFiles.\`. If those spec files are absent or empty, mark **Spec Coverage**, **Requirement Implementation Mapping**, and **Scenario Coverage** as not verified; do not treat any of them as clean. + - If delta specs exist in those spec files: - Extract all requirements (marked with "### Requirement:", or listed as \`FROM:\`/\`TO:\` pairs under \`## RENAMED Requirements\`) and note the delta section each one sits under: \`## ADDED\`, \`## MODIFIED\`, \`## REMOVED\`, or \`## RENAMED Requirements\`. The section decides what the check looks for. - For each ADDED or MODIFIED requirement (for MODIFIED, check the text in the delta, not the old wording): - Search codebase for keywords related to the requirement @@ -345,14 +345,14 @@ ${PROJECT_ROOT_GUARD} 7. **Verify Coherence** **Design Adherence**: - - If the schema defines no design artifact, report **Design Adherence** as not applicable. - - If \`contextFiles.design\` exists: + - If the schema defines no design artifact (no artifact with id \`design\`, and none whose \`artifactPaths..outputPath\` is or ends in \`design.md\`), report **Design Adherence** as not applicable. + - If the design artifact's \`contextFiles.\` file exists: - Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:") - Verify implementation follows those decisions - If contradiction detected: - Add WARNING: "Design decision not followed: " - Recommendation: "Update implementation or revise design.md to match reality" - - Otherwise, if \`contextFiles.design\` is absent or empty: mark **Design Adherence** as not verified. With other supporting artifacts, **Code Pattern Consistency** still runs; the task-only case remains limited to task completion. + - Otherwise, if the design artifact's \`contextFiles.\` file is absent or empty: mark **Design Adherence** as not verified. With other supporting artifacts, **Code Pattern Consistency** still runs; the task-only case remains limited to task completion. **Code Pattern Consistency**: - If implementation changes cannot be identified, mark **Code Pattern Consistency** as not verified and explain the missing evidence. diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index e3d1c48dc7..b1f38b96ea 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -91,11 +91,11 @@ const EXPECTED_FUNCTION_HASHES: Record = { getArchiveChangeSkillTemplate: '04a029782fc4137971fad6f54cfda3685d7e0b835b06565f9a509b4878093099', getBulkArchiveChangeSkillTemplate: '44dbd3c7a347e5f8339b2141393f2ac36017527cce251483fe70f2059c1e286e', getOpsxSyncCommandTemplate: '60550b7bb9829421656d6324a9e4c951bc912f48f88882d1a07ce7f78397a5e7', - getVerifyChangeSkillTemplate: 'eecb063792075191b613978dec45f9f2fee247d2ff3003f2ebf17d632e54352e', + getVerifyChangeSkillTemplate: '9522cdf3fd638baee6098d480445c615b908b2f35d776b2a1eebeb6e2b208480', getOpsxArchiveCommandTemplate: '5d153490bf1ca24207f49856826720f793c8884a6ec15059104107b0e34ee345', getOpsxOnboardCommandTemplate: '0cf66e164c0e14c916c6d1ebb5d80ded07d7fb8e55d4eb34eba43e8ca9c28558', getOpsxBulkArchiveCommandTemplate: 'cb1d55d6ce53686bfe94be5e081c7a4d06a8e4d10b63019132df5bb3db7144cb', - getOpsxVerifyCommandTemplate: 'f47bc0c30cfa8e93b5e42026e9417636c5f15bd8505fb9138872e34af8906abb', + getOpsxVerifyCommandTemplate: '62ba027d789186416620f963341129bf8f7acf956c8f5b806f78945e1109d7ab', getOpsxProposeSkillTemplate: '1aa2f2eb9c8cbc4dcab9d777bf8832b92ca04f9ef91d0494f1224a566aefdfe8', getOpsxProposeCommandTemplate: '3b7090ce5e79e879ab9b5bdaf4ff2b52e3c02211f71188838772d36ac337f96c', getFeedbackSkillTemplate: 'dabeb5e825b9349abc8156c3e7b8608f27987912a6d9bf47ef29addde6138133', @@ -112,7 +112,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { 'openspec-sync-specs': '3909936a236a21a9a6d5bf495f90b396b3b68fc9220d7b2c1894668653beb2e4', 'openspec-archive-change': '5f0d131a885dcdcd9ba2172ea9a42bc6748125e24b8c4eecb7c86f1a4aea83af', 'openspec-bulk-archive-change': 'd2a258055ab2f0d8086c4348d37212ebc95a5adef2d5f524db959fb93490d5c8', - 'openspec-verify-change': '62c2d471a1ebc4be38df0d06393eb94d3d8b803719b6349b8a1d8e9231448275', + 'openspec-verify-change': 'ed7c3f0287ef246fc1c19665fd64c275259712950bfe95fb94971ddcc8fb64ac', 'openspec-onboard': '6993eff867d97d485e080078f9dfb80e968e242f3b17a924eeb077715fd548fa', 'openspec-propose': '66e3395adf9f2d93a09e8ef1d20e4efb010e5e8d4811f2d42a9316e4d1ca5a8b', 'openspec-update-change': '5f4ea19aa732b33d87a2120ec393ee34578e70678d97e8c3bb10f988c00cb4d3', diff --git a/test/core/templates/verify-change.test.ts b/test/core/templates/verify-change.test.ts index 05fa002949..e2e2be7654 100644 --- a/test/core/templates/verify-change.test.ts +++ b/test/core/templates/verify-change.test.ts @@ -14,6 +14,17 @@ const bodies: Array<[string, string]> = [ ]; describe('verify-change templates', () => { + it('finds spec and design artifacts by output path, not by hardcoded artifact id', () => { + for (const [label, body] of bodies) { + expect(body, label).not.toContain('`contextFiles.specs`'); + expect(body, label).not.toContain('`contextFiles.design`'); + expect(body, label).toContain('`artifactPaths..outputPath`'); + expect(body, label).toContain('under `specs/`'); + expect(body, label).toContain('`contextFiles.`'); + expect(body, label).toContain('is or ends in `design.md`'); + } + }); + it('keeps active no-task changes eligible for ambiguous selection', () => { for (const [label, body] of bodies) { expect(body, label).toContain('show all active changes returned by the list');