diff --git a/src/core/templates/workflows/archive-change.ts b/src/core/templates/workflows/archive-change.ts index 20f69c2bff..6eceeafacf 100644 --- a/src/core/templates/workflows/archive-change.ts +++ b/src/core/templates/workflows/archive-change.ts @@ -61,6 +61,8 @@ ${STORE_SELECTION_GUIDANCE} **If delta specs exist:** - Compare each delta spec with its corresponding main spec at \`openspec/specs//spec.md\` + - If the main spec does not exist, treat the delta as an unsynced greenfield capability and create a new main spec from ADDED requirements during sync + - Do not treat a missing main spec as already synced or skip it because no existing capability was found - Determine what changes would be applied (adds, modifications, removals, renames) - Show a combined summary before prompting @@ -179,6 +181,8 @@ ${STORE_SELECTION_GUIDANCE} **If delta specs exist:** - Compare each delta spec with its corresponding main spec at \`openspec/specs//spec.md\` + - If the main spec does not exist, treat the delta as an unsynced greenfield capability and create a new main spec from ADDED requirements during sync + - Do not treat a missing main spec as already synced or skip it because no existing capability was found - Determine what changes would be applied (adds, modifications, removals, renames) - Show a combined summary before prompting diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index cc6ec7bc12..6843e0c8f9 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -47,11 +47,11 @@ const EXPECTED_FUNCTION_HASHES: Record = { getOpsxContinueCommandTemplate: '418108b417107a87019d4020b26c105792d2ef0110fe6920445e255889216716', getOpsxApplyCommandTemplate: 'daeb507206707169de73c828e199648dde5732cbc17791ef2a027adffd028574', getOpsxFfCommandTemplate: '36973ae0dd00ab169fbaaa42bf565f97e1bc97cf63ae7c07307734cc1ca8c1fd', - getArchiveChangeSkillTemplate: 'c511a1c943bcfc5f9f3833b8c0ff284b22d34864a08f5f553cec471ee485d38f', + getArchiveChangeSkillTemplate: 'c1677ff1fb3d85e3bbc52db5cda1d51bfda70f828005bbad723b5d66e57fdd1a', getBulkArchiveChangeSkillTemplate: '0f635913757ae3d1609e111f4a8f699443ca47cbaaf8a1b21eb652f7b96a1d13', getOpsxSyncCommandTemplate: '86cf706886d0f18069e2cfa16948b7357028fd348210efb58588c88c416d8622', getVerifyChangeSkillTemplate: 'd718c79aad649223a73fdb11036c93fb3842ac5a780f4934d50bfa03c9692683', - getOpsxArchiveCommandTemplate: '6985bddb310cb45b6b50350bfcebe31bf67146135ca0084c94930920280970a4', + getOpsxArchiveCommandTemplate: '8b259e71ec6c665f884d545503ca8795aecb909c577e75ba61c25ebf284fb014', getOpsxOnboardCommandTemplate: '0673f34a0f81fd173bcfb8c3ac83e2b1c617f7b7564e24e5298d3bd5665a05a9', getOpsxBulkArchiveCommandTemplate: '9f444fc7b27a5b788077b5e3aa4f61af45aa8c8004ac8d899d204fa362ff89b7', getOpsxVerifyCommandTemplate: '011509480a20a60342c993906f0f9280c0e9ba5d019d335bdc1ef4d53213a5a8', @@ -67,7 +67,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { 'openspec-apply-change': '54cffa61274c6a499d2b3775e9f6db29255fd8e5ad99d7352c1e3bbe2edb45ed', 'openspec-ff-change': 'cbb7844c130bd188319ff2b3f0c0320243b5ae5b588a0f816cd4e29408f25676', 'openspec-sync-specs': 'a81fd87f5e871874eab72e57c10a1949fde46d1d07d95f8ea3bc1a52b4e78c43', - 'openspec-archive-change': '833290ade47ddaed7f5e523d07437c7cef2497340021e944096bce449e290c22', + 'openspec-archive-change': '97eb4b9d42c21bf10b257ed83c1127881c07ecc92ca8855b038e1c8da14e4d96', 'openspec-bulk-archive-change': '244b195e53d3f010a99892c1922c800fd8f02e7745d0f34ec18b5fe9b5548706', 'openspec-verify-change': '97d1eed5b900788706c28339e27c1d2d9c548626316253f43ebd00d8d52d02d6', 'openspec-onboard': 'd136b6ab7134d6bceeca73bc2f6037624506587e8df99059f77fe88874256ed1', @@ -191,4 +191,18 @@ describe('skill templates split parity', () => { expect(content, dirName).not.toContain('Workspace guard'); } }); + + it('instructs archive workflows to create missing main specs for greenfield changes', () => { + const archiveSkill = generateSkillContent(getArchiveChangeSkillTemplate(), 'PARITY-BASELINE'); + const archiveCommand = getOpsxArchiveCommandTemplate().content; + + for (const [name, content] of [ + ['openspec-archive-change', archiveSkill], + ['opsx-archive-command', archiveCommand], + ] as const) { + expect(content, name).toContain('If the main spec does not exist'); + expect(content, name).toContain('create a new main spec'); + expect(content, name).toContain('Do not treat a missing main spec as already synced'); + } + }); });