diff --git a/src/core/templates/workflows/archive-change.ts b/src/core/templates/workflows/archive-change.ts index 41619c2b3..0390120b1 100644 --- a/src/core/templates/workflows/archive-change.ts +++ b/src/core/templates/workflows/archive-change.ts @@ -59,8 +59,10 @@ export function getArchiveChangeSkillTemplate(): SkillTemplate { Use \`artifactPaths.specs.existingOutputPaths\` from status JSON to check for delta specs. If none exist, proceed without sync prompt. **If delta specs exist:** - - Compare each delta spec with its corresponding main spec at \`openspec/specs//spec.md\` - - Determine what changes would be applied (adds, modifications, removals, renames) + - Locate each corresponding main spec at \`openspec/specs//spec.md\` + - Treat a missing main spec as "changes needed" because sync must create it from the delta spec before archive + - For existing main specs, compare each delta spec with its corresponding main spec + - Determine what changes would be applied (new specs to create, adds, modifications, removals, renames) - Show a combined summary before prompting **Prompt options:** @@ -177,8 +179,10 @@ export function getOpsxArchiveCommandTemplate(): CommandTemplate { Use \`artifactPaths.specs.existingOutputPaths\` from status JSON to check for delta specs. If none exist, proceed without sync prompt. **If delta specs exist:** - - Compare each delta spec with its corresponding main spec at \`openspec/specs//spec.md\` - - Determine what changes would be applied (adds, modifications, removals, renames) + - Locate each corresponding main spec at \`openspec/specs//spec.md\` + - Treat a missing main spec as "changes needed" because sync must create it from the delta spec before archive + - For existing main specs, compare each delta spec with its corresponding main spec + - Determine what changes would be applied (new specs to create, adds, modifications, removals, renames) - Show a combined summary before prompting **Prompt options:** diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index f851082e5..cd895ba73 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -42,11 +42,11 @@ const EXPECTED_FUNCTION_HASHES: Record = { getOpsxContinueCommandTemplate: '62f8863edda2bfe4e210f8bc3095fd4369aaaaf7772a5cba9602d0f0bca1d0c9', getOpsxApplyCommandTemplate: '812feefd32a4d9d468e03e456d06e3d2d08d1118d29cce4911f0be59cdd30bfc', getOpsxFfCommandTemplate: 'f775b242bcfd56594c431c7f31a0129208a1bacfdb2427074d412543072ef7ca', - getArchiveChangeSkillTemplate: 'bdf022ae2cdef1feef4d641a068bef3a7fc5d98a323f7ce9f77ac578fe8d20c6', + getArchiveChangeSkillTemplate: '7d87ac5d2a4a0dd4d10cec8cec28349ca6a57283aff9570c83b6f6296cfa3257', getBulkArchiveChangeSkillTemplate: 'fdb1715804e86de85be96222b8efeb9d5b350c6d5c19e343e244655deff8e62b', getOpsxSyncCommandTemplate: '4c8118afaea79ff4fed3d946c88e6a7abbba904a5fbf643e4372da1e3735a467', getVerifyChangeSkillTemplate: '3c5dda8b49ba00f50b5bae7f04763dd00cc00a05e5f1d8a2068ad7fb701d8165', - getOpsxArchiveCommandTemplate: '5181ec2f59c9f0f3376e61d952ed4be976cbd01595b6b0d5e67466c8bd6bac6d', + getOpsxArchiveCommandTemplate: '14adef4f8dce571f483148ac21e7ab420d2a795f2cc20b1b31081600205032e5', getOpsxOnboardCommandTemplate: '57c1f3e2590bda8f47818bab1d528456c1b8a9a7501f63ab9e2115e0cfaf6f35', getOpsxBulkArchiveCommandTemplate: 'b76c421023ccb5a12867c349f27cdb186234b692c1811980fb94127567bdabda', getOpsxVerifyCommandTemplate: '9a7a3f9e5bc3d0c0878b1a4493efbbb38729597d9b9be78f63284cc2da7c20c3', @@ -62,7 +62,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { 'openspec-apply-change': 'd849442efd925b9247651e254a5cd696945321610cca5a9432ad420430554548', 'openspec-ff-change': '9d9b1995b6f4adb3da570676f7d11fee4cd1cf6c5df8ec83c033e02783a544df', 'openspec-sync-specs': '2e0f67ec6fadffc6107b4b1a28eef23a99a6649e5fae706897ea1dd9deb852a8', - 'openspec-archive-change': '8d14af2c8b2e4358308ac9fc14f75db42a4b41a07e175825035852a82479793e', + 'openspec-archive-change': 'cdf8e9c7bfa8ec01d02329a420464790606e78535b3b3c62a59d915e047d38ff', 'openspec-bulk-archive-change': '16207683996b1952559cd4e33463f28fb097761f2c5d912107733d01a90d3f2f', 'openspec-verify-change': 'a2acecd0c2b4e57080a314e5e7a093e0688293c37e446eb45d378f5050058550', 'openspec-onboard': 'b924ea3c97543ebb7ee82c5f194afe7ce87a521c32b85616f445240ab33a02ab', @@ -169,4 +169,16 @@ describe('skill templates split parity', () => { expect(content, dirName).not.toContain('mv openspec/changes'); } }); + + it('treats missing main specs as sync-needed during archive', () => { + const generatedSkill = generateSkillContent(getArchiveChangeSkillTemplate(), 'PARITY-BASELINE'); + const commandContent = getOpsxArchiveCommandTemplate().content; + + for (const content of [generatedSkill, commandContent]) { + expect(content).toContain('Treat a missing main spec as "changes needed"'); + expect(content).toContain('sync must create it from the delta spec before archive'); + expect(content).toContain('new specs to create'); + expect(content).toContain('"Sync now (recommended)"'); + } + }); });