diff --git a/.changeset/sync-specs-main-spec-format.md b/.changeset/sync-specs-main-spec-format.md new file mode 100644 index 0000000000..411109f3ee --- /dev/null +++ b/.changeset/sync-specs-main-spec-format.md @@ -0,0 +1,5 @@ +--- +"@fission-ai/openspec": patch +--- + +Show the main spec format in the sync-specs skill so agents stop leaving delta operation headers (`## ADDED/MODIFIED Requirements`) in `openspec/specs/` — merged main specs with those headers parse as 0 requirements in `openspec view` (#1120). diff --git a/openspec/specs/specs-sync-skill/spec.md b/openspec/specs/specs-sync-skill/spec.md index 780c6e15b5..8cc0e081a2 100644 --- a/openspec/specs/specs-sync-skill/spec.md +++ b/openspec/specs/specs-sync-skill/spec.md @@ -56,6 +56,11 @@ The agent SHALL reconcile main specs with delta specs using the delta operation - **WHEN** delta spec exists for a capability not in main specs - **THEN** create new main spec file at `openspec/specs//spec.md` +#### Scenario: Merged main spec keeps canonical structure +- **WHEN** the agent writes a main spec during sync +- **THEN** every requirement lives under a single `## Requirements` section +- **AND** the main spec contains no delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) + ### Requirement: Skill Output The skill SHALL provide clear feedback on what was applied. diff --git a/skills/openspec-sync-specs/SKILL.md b/skills/openspec-sync-specs/SKILL.md index afdcea244a..122a7b6400 100644 --- a/skills/openspec-sync-specs/SKILL.md +++ b/skills/openspec-sync-specs/SKILL.md @@ -80,6 +80,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e - Create `/openspec/specs//spec.md` - Add Purpose section (can be brief, mark as TBD) - Add Requirements section with the ADDED requirements + - Follow the **Main Spec Format Reference** below 5. **Show summary** @@ -116,6 +117,26 @@ The system SHALL do something new. - TO: `### Requirement: New Name` ``` +**Main Spec Format Reference** + +Main specs are what the delta merges INTO. They must never contain delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) - after syncing, every requirement lives under a single `## Requirements` section: + +```markdown +# Specification + +## Purpose +Short description of what this capability does and why it exists. + +## Requirements + +### Requirement: New Feature +The system SHALL do something new. + +#### Scenario: Basic case +- **WHEN** user does X +- **THEN** system does Y +``` + **Key Principle: Intelligent Merging** Unlike programmatic merging, you can apply **partial updates**: @@ -144,6 +165,7 @@ Main specs are now updated. The change remains active - archive when implementat **Guardrails** - Read both delta and main specs before making changes - Preserve existing content not mentioned in delta +- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers - If something is unclear, ask for clarification - Show what you're changing as you go - The operation should be idempotent - running twice should give same result diff --git a/src/core/templates/workflows/sync-specs.ts b/src/core/templates/workflows/sync-specs.ts index 9a99986d01..9dc3cac26b 100644 --- a/src/core/templates/workflows/sync-specs.ts +++ b/src/core/templates/workflows/sync-specs.ts @@ -82,6 +82,7 @@ ${STORE_SELECTION_GUIDANCE} - Create \`/openspec/specs//spec.md\` - Add Purpose section (can be brief, mark as TBD) - Add Requirements section with the ADDED requirements + - Follow the **Main Spec Format Reference** below 5. **Show summary** @@ -118,6 +119,26 @@ The system SHALL do something new. - TO: \`### Requirement: New Name\` \`\`\` +**Main Spec Format Reference** + +Main specs are what the delta merges INTO. They must never contain delta operation headers (\`## ADDED/MODIFIED/REMOVED/RENAMED Requirements\`) - after syncing, every requirement lives under a single \`## Requirements\` section: + +\`\`\`markdown +# Specification + +## Purpose +Short description of what this capability does and why it exists. + +## Requirements + +### Requirement: New Feature +The system SHALL do something new. + +#### Scenario: Basic case +- **WHEN** user does X +- **THEN** system does Y +\`\`\` + **Key Principle: Intelligent Merging** Unlike programmatic merging, you can apply **partial updates**: @@ -146,6 +167,7 @@ Main specs are now updated. The change remains active - archive when implementat **Guardrails** - Read both delta and main specs before making changes - Preserve existing content not mentioned in delta +- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers - If something is unclear, ask for clarification - Show what you're changing as you go - The operation should be idempotent - running twice should give same result`, @@ -232,6 +254,7 @@ ${STORE_SELECTION_GUIDANCE} - Create \`/openspec/specs//spec.md\` - Add Purpose section (can be brief, mark as TBD) - Add Requirements section with the ADDED requirements + - Follow the **Main Spec Format Reference** below 5. **Show summary** @@ -268,6 +291,26 @@ The system SHALL do something new. - TO: \`### Requirement: New Name\` \`\`\` +**Main Spec Format Reference** + +Main specs are what the delta merges INTO. They must never contain delta operation headers (\`## ADDED/MODIFIED/REMOVED/RENAMED Requirements\`) - after syncing, every requirement lives under a single \`## Requirements\` section: + +\`\`\`markdown +# Specification + +## Purpose +Short description of what this capability does and why it exists. + +## Requirements + +### Requirement: New Feature +The system SHALL do something new. + +#### Scenario: Basic case +- **WHEN** user does X +- **THEN** system does Y +\`\`\` + **Key Principle: Intelligent Merging** Unlike programmatic merging, you can apply **partial updates**: @@ -296,6 +339,7 @@ Main specs are now updated. The change remains active - archive when implementat **Guardrails** - Read both delta and main specs before making changes - Preserve existing content not mentioned in delta +- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers - If something is unclear, ask for clarification - Show what you're changing as you go - The operation should be idempotent - running twice should give same result` diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index 0d8b783852..82ae9bcb5d 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -42,7 +42,7 @@ const EXPECTED_FUNCTION_HASHES: Record = { getContinueChangeSkillTemplate: 'acc07a489a30192b4bf2bbdc587a889478fbf6fffbbc9353c7775c4ca1ec5011', getApplyChangeSkillTemplate: '0f5a15fc7fb9ad6059a5643d0e01365d27642637a4aaebf182f9eabb45348197', getFfChangeSkillTemplate: '20ebb682ba89809a100cd4985c074908df5bada2bd649ca1b0f4059a63a1c728', - getSyncSpecsSkillTemplate: 'dc07ea0312687f3edc602329c889dbbab737c6d79327eb7a723553d346b43433', + getSyncSpecsSkillTemplate: '32c3169e1ee0345a174c0bacb8fd16db73477cc006d8cedbedc6077233c5461b', getOnboardSkillTemplate: 'e871d8ce172bb805ae62a7611aee7a3154d89414f427ad5ef31721c903f13002', getOpsxExploreCommandTemplate: '37e53590aae7ac6621d4393aa80a5b8af21881323887fa924ed329199fda27e0', getOpsxNewCommandTemplate: '57c600cce318d16b9b4308a18d0d983ea3c0673034e606a7cceec07b4c705e87', @@ -51,7 +51,7 @@ const EXPECTED_FUNCTION_HASHES: Record = { getOpsxFfCommandTemplate: 'b859b1955cda6012877ae7f9ec6980e468f2e949a3838dfcdebc17209d133749', getArchiveChangeSkillTemplate: 'a8f1d9cb06c20c7335ac35826dd09bfadead75ef6d624d359912734f74232cbc', getBulkArchiveChangeSkillTemplate: 'f675122bce3ef583b245352abedecf50ff4043e45bea6bac091885f83c7b6362', - getOpsxSyncCommandTemplate: '98b20e00da5c588ff83ed6e6f0e959dfc540349090fb3f5792ea030d099b8169', + getOpsxSyncCommandTemplate: '68dc44c9be2ec1ef719a4ed59830e5a0bc74c3ba6113070650266e1b0d153071', getVerifyChangeSkillTemplate: 'cab4db01b5d2b1243d63d90c53747d8b39e488c60f76eba3fe8b994467f69267', getOpsxArchiveCommandTemplate: '9d14e1ea23ae8be8971fafa1d6a4d4717a8a7b922b6e76c6fb07aa568a420632', getOpsxOnboardCommandTemplate: '0673f34a0f81fd173bcfb8c3ac83e2b1c617f7b7564e24e5298d3bd5665a05a9', @@ -70,7 +70,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { 'openspec-continue-change': 'bdb8bbb6a768a741b05256effbc284d65ac6a45360b59c24b94198792d3d0ebf', 'openspec-apply-change': '09c0e1cdf5ccc82416d0969d6bd715cc70616bdbc3531358a5c36057f78be55a', 'openspec-ff-change': '0c82830cd9bc98f86eb56b63ddaabe2bf5d35fe25b6c40a7059311aee2c8acac', - 'openspec-sync-specs': 'b3f694ab81956d05126b089fe82dea78dec21788978bb9651485f996aee96740', + 'openspec-sync-specs': 'd1bcd420bf8fb55a13f58a2857e6ebde58eb6f9e721a3bf6876bd9f640a63859', 'openspec-archive-change': '4679a077d34016bf38f0d0aa5432b53ea83ae82c2c5fec6dcb7dc15571ee8ac6', 'openspec-bulk-archive-change': '545b9528df52fbb0b4898405b42a2ce10416678d469d20cf597d022fa6e16e3b', 'openspec-verify-change': '57693d22940f06080c6cf8d590ac2f48240d4a5e9ce7074dacd0f8d3c9945afa',