@fission-ai/openspec 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/README.md +9 -6
  2. package/bin/openspec.js +3 -1
  3. package/dist/cli/index.d.ts +4 -1
  4. package/dist/cli/index.js +46 -2
  5. package/dist/commands/completion.js +7 -0
  6. package/dist/commands/config.js +64 -5
  7. package/dist/commands/context-store.d.ts +3 -0
  8. package/dist/commands/context-store.js +475 -0
  9. package/dist/commands/initiative.d.ts +13 -0
  10. package/dist/commands/initiative.js +318 -0
  11. package/dist/commands/workflow/index.d.ts +2 -0
  12. package/dist/commands/workflow/index.js +1 -0
  13. package/dist/commands/workflow/initiative-link.d.ts +24 -0
  14. package/dist/commands/workflow/initiative-link.js +47 -0
  15. package/dist/commands/workflow/instructions.d.ts +1 -1
  16. package/dist/commands/workflow/instructions.js +46 -83
  17. package/dist/commands/workflow/new-change.d.ts +6 -0
  18. package/dist/commands/workflow/new-change.js +114 -17
  19. package/dist/commands/workflow/set-change.d.ts +13 -0
  20. package/dist/commands/workflow/set-change.js +87 -0
  21. package/dist/commands/workflow/shared.d.ts +5 -3
  22. package/dist/commands/workflow/shared.js +6 -6
  23. package/dist/commands/workflow/status.js +24 -9
  24. package/dist/commands/workflow/templates.js +5 -4
  25. package/dist/commands/workspace/context-status.d.ts +4 -0
  26. package/dist/commands/workspace/context-status.js +59 -0
  27. package/dist/commands/workspace/open-target-selection.d.ts +13 -0
  28. package/dist/commands/workspace/open-target-selection.js +146 -0
  29. package/dist/commands/workspace/open-view.d.ts +62 -0
  30. package/dist/commands/workspace/open-view.js +249 -0
  31. package/dist/commands/workspace/open.d.ts +37 -0
  32. package/dist/commands/workspace/open.js +110 -0
  33. package/dist/commands/workspace/opener-selection.d.ts +11 -0
  34. package/dist/commands/workspace/opener-selection.js +98 -0
  35. package/dist/commands/workspace/operations.d.ts +29 -0
  36. package/dist/commands/workspace/operations.js +543 -0
  37. package/dist/commands/workspace/prompt-theme.d.ts +29 -0
  38. package/dist/commands/workspace/prompt-theme.js +24 -0
  39. package/dist/commands/workspace/registration.d.ts +13 -0
  40. package/dist/commands/workspace/registration.js +84 -0
  41. package/dist/commands/workspace/selection.d.ts +9 -0
  42. package/dist/commands/workspace/selection.js +115 -0
  43. package/dist/commands/workspace/setup-prompts.d.ts +13 -0
  44. package/dist/commands/workspace/setup-prompts.js +121 -0
  45. package/dist/commands/workspace/types.d.ts +103 -0
  46. package/dist/commands/workspace/types.js +36 -0
  47. package/dist/commands/workspace.d.ts +6 -0
  48. package/dist/commands/workspace.js +587 -0
  49. package/dist/core/artifact-graph/index.d.ts +3 -1
  50. package/dist/core/artifact-graph/index.js +1 -0
  51. package/dist/core/artifact-graph/instruction-loader.d.ts +41 -1
  52. package/dist/core/artifact-graph/instruction-loader.js +51 -9
  53. package/dist/core/artifact-graph/outputs.d.ts +14 -0
  54. package/dist/core/artifact-graph/outputs.js +39 -0
  55. package/dist/core/artifact-graph/state.js +2 -25
  56. package/dist/core/artifact-graph/types.d.ts +0 -5
  57. package/dist/core/artifact-graph/types.js +0 -14
  58. package/dist/core/change-metadata/index.d.ts +2 -0
  59. package/dist/core/change-metadata/index.js +2 -0
  60. package/dist/core/change-metadata/schema.d.ts +18 -0
  61. package/dist/core/change-metadata/schema.js +28 -0
  62. package/dist/core/change-status-policy.d.ts +50 -0
  63. package/dist/core/change-status-policy.js +70 -0
  64. package/dist/core/collections/index.d.ts +3 -0
  65. package/dist/core/collections/index.js +3 -0
  66. package/dist/core/collections/initiatives/collection.d.ts +4 -0
  67. package/dist/core/collections/initiatives/collection.js +17 -0
  68. package/dist/core/collections/initiatives/index.d.ts +6 -0
  69. package/dist/core/collections/initiatives/index.js +6 -0
  70. package/dist/core/collections/initiatives/operations.d.ts +49 -0
  71. package/dist/core/collections/initiatives/operations.js +175 -0
  72. package/dist/core/collections/initiatives/resolution.d.ts +87 -0
  73. package/dist/core/collections/initiatives/resolution.js +374 -0
  74. package/dist/core/collections/initiatives/schema.d.ts +41 -0
  75. package/dist/core/collections/initiatives/schema.js +134 -0
  76. package/dist/core/collections/initiatives/templates.d.ts +12 -0
  77. package/dist/core/collections/initiatives/templates.js +90 -0
  78. package/dist/core/collections/runtime.d.ts +46 -0
  79. package/dist/core/collections/runtime.js +194 -0
  80. package/dist/core/completions/command-registry.d.ts +1 -5
  81. package/dist/core/completions/command-registry.js +532 -32
  82. package/dist/core/completions/completion-provider.d.ts +11 -0
  83. package/dist/core/completions/completion-provider.js +27 -0
  84. package/dist/core/completions/generators/bash-generator.d.ts +3 -0
  85. package/dist/core/completions/generators/bash-generator.js +60 -4
  86. package/dist/core/completions/generators/fish-generator.js +3 -0
  87. package/dist/core/completions/generators/powershell-generator.d.ts +3 -0
  88. package/dist/core/completions/generators/powershell-generator.js +63 -4
  89. package/dist/core/completions/generators/zsh-generator.d.ts +3 -0
  90. package/dist/core/completions/generators/zsh-generator.js +46 -22
  91. package/dist/core/completions/installers/zsh-installer.d.ts +0 -8
  92. package/dist/core/completions/installers/zsh-installer.js +3 -31
  93. package/dist/core/completions/shared-flags.d.ts +12 -0
  94. package/dist/core/completions/shared-flags.js +28 -0
  95. package/dist/core/completions/templates/bash-templates.d.ts +1 -1
  96. package/dist/core/completions/templates/bash-templates.js +6 -0
  97. package/dist/core/completions/templates/fish-templates.d.ts +1 -1
  98. package/dist/core/completions/templates/fish-templates.js +6 -0
  99. package/dist/core/completions/templates/powershell-templates.d.ts +1 -1
  100. package/dist/core/completions/templates/powershell-templates.js +9 -0
  101. package/dist/core/completions/templates/zsh-templates.d.ts +1 -1
  102. package/dist/core/completions/templates/zsh-templates.js +9 -0
  103. package/dist/core/completions/types.d.ts +23 -1
  104. package/dist/core/config.js +3 -1
  105. package/dist/core/context-store/binding.d.ts +53 -0
  106. package/dist/core/context-store/binding.js +197 -0
  107. package/dist/core/context-store/errors.d.ts +20 -0
  108. package/dist/core/context-store/errors.js +22 -0
  109. package/dist/core/context-store/foundation.d.ts +55 -0
  110. package/dist/core/context-store/foundation.js +321 -0
  111. package/dist/core/context-store/index.d.ts +6 -0
  112. package/dist/core/context-store/index.js +6 -0
  113. package/dist/core/context-store/operations.d.ts +85 -0
  114. package/dist/core/context-store/operations.js +528 -0
  115. package/dist/core/context-store/registry.d.ts +45 -0
  116. package/dist/core/context-store/registry.js +229 -0
  117. package/dist/core/global-config.d.ts +6 -1
  118. package/dist/core/global-config.js +15 -16
  119. package/dist/core/index.d.ts +5 -1
  120. package/dist/core/index.js +4 -0
  121. package/dist/core/parsers/change-parser.js +7 -3
  122. package/dist/core/parsers/markdown-parser.d.ts +4 -0
  123. package/dist/core/parsers/markdown-parser.js +41 -1
  124. package/dist/core/parsers/requirement-blocks.js +5 -5
  125. package/dist/core/parsers/spec-structure.d.ts +9 -0
  126. package/dist/core/parsers/spec-structure.js +88 -0
  127. package/dist/core/planning-home.d.ts +21 -0
  128. package/dist/core/planning-home.js +108 -0
  129. package/dist/core/profiles.d.ts +1 -1
  130. package/dist/core/profiles.js +1 -1
  131. package/dist/core/specs-apply.js +9 -1
  132. package/dist/core/templates/workflows/apply-change.js +10 -4
  133. package/dist/core/templates/workflows/archive-change.js +21 -15
  134. package/dist/core/templates/workflows/bulk-archive-change.js +14 -10
  135. package/dist/core/templates/workflows/continue-change.js +6 -4
  136. package/dist/core/templates/workflows/explore.js +8 -10
  137. package/dist/core/templates/workflows/ff-change.js +8 -6
  138. package/dist/core/templates/workflows/new-change.js +6 -6
  139. package/dist/core/templates/workflows/onboard.js +13 -15
  140. package/dist/core/templates/workflows/propose.js +8 -6
  141. package/dist/core/templates/workflows/sync-specs.js +28 -10
  142. package/dist/core/templates/workflows/verify-change.js +16 -10
  143. package/dist/core/update.d.ts +5 -0
  144. package/dist/core/update.js +20 -0
  145. package/dist/core/validation/validator.d.ts +11 -0
  146. package/dist/core/validation/validator.js +28 -2
  147. package/dist/core/workspace/foundation.d.ts +67 -0
  148. package/dist/core/workspace/foundation.js +295 -0
  149. package/dist/core/workspace/index.d.ts +8 -0
  150. package/dist/core/workspace/index.js +8 -0
  151. package/dist/core/workspace/legacy-state.d.ts +28 -0
  152. package/dist/core/workspace/legacy-state.js +200 -0
  153. package/dist/core/workspace/link-input.d.ts +9 -0
  154. package/dist/core/workspace/link-input.js +32 -0
  155. package/dist/core/workspace/open-surface.d.ts +45 -0
  156. package/dist/core/workspace/open-surface.js +215 -0
  157. package/dist/core/workspace/openers.d.ts +21 -0
  158. package/dist/core/workspace/openers.js +124 -0
  159. package/dist/core/workspace/registry.d.ts +24 -0
  160. package/dist/core/workspace/registry.js +146 -0
  161. package/dist/core/workspace/skills.d.ts +57 -0
  162. package/dist/core/workspace/skills.js +334 -0
  163. package/dist/core/workspace/state-io.d.ts +10 -0
  164. package/dist/core/workspace/state-io.js +119 -0
  165. package/dist/telemetry/config.d.ts +7 -1
  166. package/dist/telemetry/config.js +87 -19
  167. package/dist/telemetry/index.js +19 -0
  168. package/dist/utils/change-metadata.d.ts +5 -2
  169. package/dist/utils/change-metadata.js +7 -13
  170. package/dist/utils/change-utils.d.ts +9 -0
  171. package/dist/utils/change-utils.js +7 -5
  172. package/dist/utils/file-system.d.ts +5 -0
  173. package/dist/utils/file-system.js +21 -1
  174. package/package.json +2 -1
  175. package/schemas/workspace-planning/schema.yaml +72 -0
  176. package/schemas/workspace-planning/templates/design.md +33 -0
  177. package/schemas/workspace-planning/templates/proposal.md +28 -0
  178. package/schemas/workspace-planning/templates/spec.md +9 -0
  179. package/schemas/workspace-planning/templates/tasks.md +15 -0
@@ -167,7 +167,7 @@ Now let's create a change to hold our work.
167
167
  \`\`\`
168
168
  ## Creating a Change
169
169
 
170
- A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives in \`openspec/changes/<name>/\` and holds your artifacts—proposal, specs, design, tasks.
170
+ A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives at the \`changeRoot\` reported by \`openspec status --change "<name>" --json\` and holds your artifacts—proposal, specs, design, tasks.
171
171
 
172
172
  Let me create one for our task.
173
173
  \`\`\`
@@ -179,11 +179,11 @@ openspec new change "<derived-name>"
179
179
 
180
180
  **SHOW:**
181
181
  \`\`\`
182
- Created: \`openspec/changes/<name>/\`
182
+ Created: <changeRoot from status JSON>
183
183
 
184
184
  The folder structure:
185
185
  \`\`\`
186
- openspec/changes/<name>/
186
+ <changeRoot>/
187
187
  ├── proposal.md ← Why we're doing this (empty, we'll fill it)
188
188
  ├── design.md ← How we'll build it (empty)
189
189
  ├── specs/ ← Detailed requirements (empty)
@@ -245,7 +245,7 @@ After approval, save the proposal:
245
245
  \`\`\`bash
246
246
  openspec instructions proposal --change "<name>" --json
247
247
  \`\`\`
248
- Then write the content to \`openspec/changes/<name>/proposal.md\`.
248
+ Then write the content to the \`resolvedOutputPath\` from \`openspec instructions proposal --change "<name>" --json\`.
249
249
 
250
250
  \`\`\`
251
251
  Proposal saved. This is your "why" document—you can always come back and refine it as understanding evolves.
@@ -266,12 +266,10 @@ Specs define **what** we're building in precise, testable terms. They use a requ
266
266
  For a small task like this, we might only need one spec file.
267
267
  \`\`\`
268
268
 
269
- **DO:** Create the spec file:
269
+ **DO:** Resolve where the spec file should be created:
270
270
  \`\`\`bash
271
- # Unix/macOS
272
- mkdir -p openspec/changes/<name>/specs/<capability-name>
273
- # Windows (PowerShell)
274
- # New-Item -ItemType Directory -Force -Path "openspec/changes/<name>/specs/<capability-name>"
271
+ openspec instructions specs --change "<name>" --json
272
+ # Use resolvedOutputPath from the JSON. If it is a glob, choose the concrete file path using the schema instruction and workspace planning context.
275
273
  \`\`\`
276
274
 
277
275
  Draft the spec content:
@@ -298,7 +296,7 @@ Here's the spec:
298
296
  This format—WHEN/THEN/AND—makes requirements testable. You can literally read them as test cases.
299
297
  \`\`\`
300
298
 
301
- Save to \`openspec/changes/<name>/specs/<capability>/spec.md\`.
299
+ Save to the concrete file path chosen from \`resolvedOutputPath\`.
302
300
 
303
301
  ---
304
302
 
@@ -343,7 +341,7 @@ Here's the design:
343
341
  For a small task, this captures the key decisions without over-engineering.
344
342
  \`\`\`
345
343
 
346
- Save to \`openspec/changes/<name>/design.md\`.
344
+ Save to the \`resolvedOutputPath\` from \`openspec instructions design --change "<name>" --json\`.
347
345
 
348
346
  ---
349
347
 
@@ -381,7 +379,7 @@ Each checkbox becomes a unit of work in the apply phase. Ready to implement?
381
379
 
382
380
  **PAUSE** - Wait for user to confirm they're ready to implement.
383
381
 
384
- Save to \`openspec/changes/<name>/tasks.md\`.
382
+ Save to the \`resolvedOutputPath\` from \`openspec instructions tasks --change "<name>" --json\`.
385
383
 
386
384
  ---
387
385
 
@@ -425,7 +423,7 @@ The change is implemented! One more step—let's archive it.
425
423
  \`\`\`
426
424
  ## Archiving
427
425
 
428
- When a change is complete, we archive it. This moves it from \`openspec/changes/\` to \`openspec/changes/archive/YYYY-MM-DD-<name>/\`.
426
+ When a change is complete, we archive it. The archive path is derived from \`planningHome.changesDir\` and the date.
429
427
 
430
428
  Archived changes become your project's decision history—you can always find them later to understand why something was built a certain way.
431
429
  \`\`\`
@@ -437,7 +435,7 @@ openspec archive "<name>"
437
435
 
438
436
  **SHOW:**
439
437
  \`\`\`
440
- Archived to: \`openspec/changes/archive/YYYY-MM-DD-<name>/\`
438
+ Archived to: \`<planningHome.changesDir>/archive/YYYY-MM-DD-<name>/\`
441
439
 
442
440
  The change is now part of your project's history. The code is in your codebase, the decision record is preserved.
443
441
  \`\`\`
@@ -500,7 +498,7 @@ Try \`/opsx:propose\` on something you actually want to build. You've got the rh
500
498
  If the user says they need to stop, want to pause, or seem disengaged:
501
499
 
502
500
  \`\`\`
503
- No problem! Your change is saved at \`openspec/changes/<name>/\`.
501
+ No problem! Your change is saved at the \`changeRoot\` reported by \`openspec status --change "<name>" --json\`.
504
502
 
505
503
  To pick up where we left off later:
506
504
  - \`/opsx:continue <name>\` - Resume artifact creation
@@ -30,7 +30,7 @@ When ready to implement, run /opsx:apply
30
30
  \`\`\`bash
31
31
  openspec new change "<name>"
32
32
  \`\`\`
33
- This creates a scaffolded change at \`openspec/changes/<name>/\` with \`.openspec.yaml\`.
33
+ This creates a scaffolded change in the planning home resolved by the CLI with \`.openspec.yaml\`.
34
34
 
35
35
  3. **Get the artifact build order**
36
36
  \`\`\`bash
@@ -39,6 +39,7 @@ When ready to implement, run /opsx:apply
39
39
  Parse the JSON to get:
40
40
  - \`applyRequires\`: array of artifact IDs needed before implementation (e.g., \`["tasks"]\`)
41
41
  - \`artifacts\`: list of all artifacts with their status and dependencies
42
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
42
43
 
43
44
  4. **Create artifacts in sequence until apply-ready**
44
45
 
@@ -56,10 +57,10 @@ When ready to implement, run /opsx:apply
56
57
  - \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
57
58
  - \`template\`: The structure to use for your output file
58
59
  - \`instruction\`: Schema-specific guidance for this artifact type
59
- - \`outputPath\`: Where to write the artifact
60
+ - \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
60
61
  - \`dependencies\`: Completed artifacts to read for context
61
62
  - Read any completed dependency files for context
62
- - Create the artifact file using \`template\` as the structure
63
+ - Create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`
63
64
  - Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
64
65
  - Show brief progress: "Created <artifact-id>"
65
66
 
@@ -140,7 +141,7 @@ When ready to implement, run /opsx:apply
140
141
  \`\`\`bash
141
142
  openspec new change "<name>"
142
143
  \`\`\`
143
- This creates a scaffolded change at \`openspec/changes/<name>/\` with \`.openspec.yaml\`.
144
+ This creates a scaffolded change in the planning home resolved by the CLI with \`.openspec.yaml\`.
144
145
 
145
146
  3. **Get the artifact build order**
146
147
  \`\`\`bash
@@ -149,6 +150,7 @@ When ready to implement, run /opsx:apply
149
150
  Parse the JSON to get:
150
151
  - \`applyRequires\`: array of artifact IDs needed before implementation (e.g., \`["tasks"]\`)
151
152
  - \`artifacts\`: list of all artifacts with their status and dependencies
153
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context. Use these instead of assuming repo-local paths.
152
154
 
153
155
  4. **Create artifacts in sequence until apply-ready**
154
156
 
@@ -166,10 +168,10 @@ When ready to implement, run /opsx:apply
166
168
  - \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output)
167
169
  - \`template\`: The structure to use for your output file
168
170
  - \`instruction\`: Schema-specific guidance for this artifact type
169
- - \`outputPath\`: Where to write the artifact
171
+ - \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
170
172
  - \`dependencies\`: Completed artifacts to read for context
171
173
  - Read any completed dependency files for context
172
- - Create the artifact file using \`template\` as the structure
174
+ - Create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`
173
175
  - Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
174
176
  - Show brief progress: "Created <artifact-id>"
175
177
 
@@ -18,9 +18,18 @@ This is an **agent-driven** operation - you will read delta specs and directly e
18
18
 
19
19
  **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
20
20
 
21
- 2. **Find delta specs**
21
+ 2. **Resolve change context**
22
22
 
23
- Look for delta spec files in \`openspec/changes/<name>/specs/*/spec.md\`.
23
+ Run:
24
+ \`\`\`bash
25
+ openspec status --change "<name>" --json
26
+ \`\`\`
27
+
28
+ If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace spec sync is not supported in this slice and STOP. Do not fall back to repo-local paths or edit linked repos.
29
+
30
+ 3. **Find delta specs**
31
+
32
+ Use \`artifactPaths.specs.existingOutputPaths\` from the status JSON as the list of delta spec files.
24
33
 
25
34
  Each delta spec file contains sections like:
26
35
  - \`## ADDED Requirements\` - New requirements to add
@@ -30,9 +39,9 @@ This is an **agent-driven** operation - you will read delta specs and directly e
30
39
 
31
40
  If no delta specs found, inform user and stop.
32
41
 
33
- 3. **For each delta spec, apply changes to main specs**
42
+ 4. **For each delta spec, apply changes to main specs**
34
43
 
35
- For each capability with a delta spec at \`openspec/changes/<name>/specs/<capability>/spec.md\`:
44
+ For each repo-local capability delta spec path returned by the CLI:
36
45
 
37
46
  a. **Read the delta spec** to understand the intended changes
38
47
 
@@ -63,7 +72,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e
63
72
  - Add Purpose section (can be brief, mark as TBD)
64
73
  - Add Requirements section with the ADDED requirements
65
74
 
66
- 4. **Show summary**
75
+ 5. **Show summary**
67
76
 
68
77
  After applying all changes, summarize:
69
78
  - Which capabilities were updated
@@ -156,9 +165,18 @@ This is an **agent-driven** operation - you will read delta specs and directly e
156
165
 
157
166
  **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
158
167
 
159
- 2. **Find delta specs**
168
+ 2. **Resolve change context**
169
+
170
+ Run:
171
+ \`\`\`bash
172
+ openspec status --change "<name>" --json
173
+ \`\`\`
174
+
175
+ If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace spec sync is not supported in this slice and STOP. Do not fall back to repo-local paths or edit linked repos.
176
+
177
+ 3. **Find delta specs**
160
178
 
161
- Look for delta spec files in \`openspec/changes/<name>/specs/*/spec.md\`.
179
+ Use \`artifactPaths.specs.existingOutputPaths\` from the status JSON as the list of delta spec files.
162
180
 
163
181
  Each delta spec file contains sections like:
164
182
  - \`## ADDED Requirements\` - New requirements to add
@@ -168,9 +186,9 @@ This is an **agent-driven** operation - you will read delta specs and directly e
168
186
 
169
187
  If no delta specs found, inform user and stop.
170
188
 
171
- 3. **For each delta spec, apply changes to main specs**
189
+ 4. **For each delta spec, apply changes to main specs**
172
190
 
173
- For each capability with a delta spec at \`openspec/changes/<name>/specs/<capability>/spec.md\`:
191
+ For each repo-local capability delta spec path returned by the CLI:
174
192
 
175
193
  a. **Read the delta spec** to understand the intended changes
176
194
 
@@ -201,7 +219,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e
201
219
  - Add Purpose section (can be brief, mark as TBD)
202
220
  - Add Requirements section with the ADDED requirements
203
221
 
204
- 4. **Show summary**
222
+ 5. **Show summary**
205
223
 
206
224
  After applying all changes, summarize:
207
225
  - Which capabilities were updated
@@ -24,15 +24,18 @@ export function getVerifyChangeSkillTemplate() {
24
24
  \`\`\`
25
25
  Parse the JSON to understand:
26
26
  - \`schemaName\`: The workflow being used (e.g., "spec-driven")
27
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
27
28
  - Which artifacts exist for this change
28
29
 
29
- 3. **Get the change directory and load artifacts**
30
+ If status reports \`actionContext.mode: "workspace-planning"\`, explain that full workspace implementation verification is not supported in this slice and STOP. Do not infer repo-local implementation ownership or edit linked repos.
31
+
32
+ 3. **Get planning context and load artifacts**
30
33
 
31
34
  \`\`\`bash
32
35
  openspec instructions apply --change "<name>" --json
33
36
  \`\`\`
34
37
 
35
- This returns the change directory and context files. Read all available artifacts from \`contextFiles\`.
38
+ This returns the change directory and \`contextFiles\` (artifact ID -> array of concrete file paths). Read all available artifacts from \`contextFiles\`.
36
39
 
37
40
  4. **Initialize verification report structure**
38
41
 
@@ -46,7 +49,7 @@ export function getVerifyChangeSkillTemplate() {
46
49
  5. **Verify Completeness**
47
50
 
48
51
  **Task Completion**:
49
- - If tasks.md exists in contextFiles, read it
52
+ - If \`contextFiles.tasks\` exists, read every file path in it
50
53
  - Parse checkboxes: \`- [ ]\` (incomplete) vs \`- [x]\` (complete)
51
54
  - Count complete vs total tasks
52
55
  - If incomplete tasks exist:
@@ -54,7 +57,7 @@ export function getVerifyChangeSkillTemplate() {
54
57
  - Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
55
58
 
56
59
  **Spec Coverage**:
57
- - If delta specs exist in \`openspec/changes/<name>/specs/\`:
60
+ - If delta specs exist in \`contextFiles.specs\`:
58
61
  - Extract all requirements (marked with "### Requirement:")
59
62
  - For each requirement:
60
63
  - Search codebase for keywords related to the requirement
@@ -85,7 +88,7 @@ export function getVerifyChangeSkillTemplate() {
85
88
  7. **Verify Coherence**
86
89
 
87
90
  **Design Adherence**:
88
- - If design.md exists in contextFiles:
91
+ - If \`contextFiles.design\` exists:
89
92
  - Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
90
93
  - Verify implementation follows those decisions
91
94
  - If contradiction detected:
@@ -192,15 +195,18 @@ export function getOpsxVerifyCommandTemplate() {
192
195
  \`\`\`
193
196
  Parse the JSON to understand:
194
197
  - \`schemaName\`: The workflow being used (e.g., "spec-driven")
198
+ - \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
195
199
  - Which artifacts exist for this change
196
200
 
197
- 3. **Get the change directory and load artifacts**
201
+ If status reports \`actionContext.mode: "workspace-planning"\`, explain that full workspace implementation verification is not supported in this slice and STOP. Do not infer repo-local implementation ownership or edit linked repos.
202
+
203
+ 3. **Get planning context and load artifacts**
198
204
 
199
205
  \`\`\`bash
200
206
  openspec instructions apply --change "<name>" --json
201
207
  \`\`\`
202
208
 
203
- This returns the change directory and context files. Read all available artifacts from \`contextFiles\`.
209
+ This returns the change directory and \`contextFiles\` (artifact ID -> array of concrete file paths). Read all available artifacts from \`contextFiles\`.
204
210
 
205
211
  4. **Initialize verification report structure**
206
212
 
@@ -214,7 +220,7 @@ export function getOpsxVerifyCommandTemplate() {
214
220
  5. **Verify Completeness**
215
221
 
216
222
  **Task Completion**:
217
- - If tasks.md exists in contextFiles, read it
223
+ - If \`contextFiles.tasks\` exists, read every file path in it
218
224
  - Parse checkboxes: \`- [ ]\` (incomplete) vs \`- [x]\` (complete)
219
225
  - Count complete vs total tasks
220
226
  - If incomplete tasks exist:
@@ -222,7 +228,7 @@ export function getOpsxVerifyCommandTemplate() {
222
228
  - Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
223
229
 
224
230
  **Spec Coverage**:
225
- - If delta specs exist in \`openspec/changes/<name>/specs/\`:
231
+ - If delta specs exist in \`contextFiles.specs\`:
226
232
  - Extract all requirements (marked with "### Requirement:")
227
233
  - For each requirement:
228
234
  - Search codebase for keywords related to the requirement
@@ -253,7 +259,7 @@ export function getOpsxVerifyCommandTemplate() {
253
259
  7. **Verify Coherence**
254
260
 
255
261
  **Design Adherence**:
256
- - If design.md exists in contextFiles:
262
+ - If \`contextFiles.design\` exists:
257
263
  - Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
258
264
  - Verify implementation follows those decisions
259
265
  - If contradiction detected:
@@ -38,6 +38,11 @@ export declare class UpdateCommand {
38
38
  * Displays a note about extra workflows installed that aren't in the current profile.
39
39
  */
40
40
  private displayExtraWorkflowsNote;
41
+ /**
42
+ * Suggest opting back into core when a custom profile still matches the old
43
+ * pre-sync core set. Keep custom profiles user-owned; do not mutate them.
44
+ */
45
+ private displayOldCoreCustomProfileNote;
41
46
  /**
42
47
  * Removes skill directories for workflows when delivery changed to commands-only.
43
48
  * Returns the number of directories removed.
@@ -23,6 +23,7 @@ import { WORKFLOW_TO_SKILL_DIR, getCommandConfiguredTools, getConfiguredToolsFor
23
23
  import { scanInstalledWorkflows as scanInstalledWorkflowsShared, migrateIfNeeded as migrateIfNeededShared, } from './migration.js';
24
24
  const require = createRequire(import.meta.url);
25
25
  const { version: OPENSPEC_VERSION } = require('../../package.json');
26
+ const OLD_CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'archive'];
26
27
  /**
27
28
  * Scans installed workflow artifacts (skills and managed commands) across all configured tools.
28
29
  * Returns the union of detected workflow IDs that match ALL_WORKFLOWS.
@@ -95,6 +96,7 @@ export class UpdateCommand {
95
96
  // Still check for new tool directories and extra workflows
96
97
  this.detectNewTools(resolvedProjectPath, configuredTools);
97
98
  this.displayExtraWorkflowsNote(resolvedProjectPath, configuredTools, desiredWorkflows);
99
+ this.displayOldCoreCustomProfileNote(profile, globalConfig.workflows);
98
100
  return;
99
101
  }
100
102
  // 8. Display update plan
@@ -201,6 +203,7 @@ export class UpdateCommand {
201
203
  this.detectNewTools(resolvedProjectPath, configuredAndNewTools);
202
204
  // 14. Display note about extra workflows not in profile
203
205
  this.displayExtraWorkflowsNote(resolvedProjectPath, configuredAndNewTools, desiredWorkflows);
206
+ this.displayOldCoreCustomProfileNote(profile, globalConfig.workflows);
204
207
  // 15. List affected tools
205
208
  if (updatedTools.length > 0) {
206
209
  const toolDisplayNames = updatedTools;
@@ -264,6 +267,23 @@ export class UpdateCommand {
264
267
  console.log(chalk.dim(`Note: ${extraWorkflows.length} extra workflows not in profile (use \`openspec config profile\` to manage)`));
265
268
  }
266
269
  }
270
+ /**
271
+ * Suggest opting back into core when a custom profile still matches the old
272
+ * pre-sync core set. Keep custom profiles user-owned; do not mutate them.
273
+ */
274
+ displayOldCoreCustomProfileNote(profile, workflows) {
275
+ if (profile !== 'custom' || !workflows) {
276
+ return;
277
+ }
278
+ const workflowSet = new Set(workflows);
279
+ const matchesOldCore = workflowSet.size === OLD_CORE_WORKFLOWS.length &&
280
+ OLD_CORE_WORKFLOWS.every((workflow) => workflowSet.has(workflow));
281
+ if (!matchesOldCore) {
282
+ return;
283
+ }
284
+ console.log(chalk.dim('Note: The core profile now includes sync. Your custom profile is preserving the old core workflow set.'));
285
+ console.log(chalk.dim('Run `openspec config profile core` and then `openspec update` to add sync.'));
286
+ }
267
287
  /**
268
288
  * Removes skill directories for workflows when delivery changed to commands-only.
269
289
  * Returns the number of directories removed.
@@ -27,6 +27,17 @@ export declare class Validator {
27
27
  isValid(report: ValidationReport): boolean;
28
28
  private extractRequirementText;
29
29
  private containsShallOrMust;
30
+ /**
31
+ * Build an error message for a requirement block whose body lacks SHALL/MUST.
32
+ *
33
+ * When the SHALL/MUST keyword already appears in the requirement header (e.g.
34
+ * `### Requirement: The system SHALL ...`) the original generic error
35
+ * ("must contain SHALL or MUST") is confusing because the keyword is visibly
36
+ * present in the spec. Per the OpenSpec conventions the keyword has to live
37
+ * on the requirement body line (the line right after the header), so we point
38
+ * the author at that exact fix when the keyword is found in the header only.
39
+ */
40
+ private buildMissingShallOrMustMessage;
30
41
  private countScenarios;
31
42
  private formatSectionList;
32
43
  }
@@ -5,6 +5,7 @@ import { MarkdownParser } from '../parsers/markdown-parser.js';
5
5
  import { ChangeParser } from '../parsers/change-parser.js';
6
6
  import { MIN_PURPOSE_LENGTH, MAX_REQUIREMENT_TEXT_LENGTH, VALIDATION_MESSAGES } from './constants.js';
7
7
  import { parseDeltaSpec, normalizeRequirementName } from '../parsers/requirement-blocks.js';
8
+ import { findMainSpecStructureIssues } from '../parsers/spec-structure.js';
8
9
  import { FileSystemUtils } from '../../utils/file-system.js';
9
10
  export class Validator {
10
11
  strictMode;
@@ -149,7 +150,7 @@ export class Validator {
149
150
  issues.push({ level: 'ERROR', path: entryPath, message: `ADDED "${block.name}" is missing requirement text` });
150
151
  }
151
152
  else if (!this.containsShallOrMust(requirementText)) {
152
- issues.push({ level: 'ERROR', path: entryPath, message: `ADDED "${block.name}" must contain SHALL or MUST` });
153
+ issues.push({ level: 'ERROR', path: entryPath, message: this.buildMissingShallOrMustMessage('ADDED', block.name) });
153
154
  }
154
155
  const scenarioCount = this.countScenarios(block.raw);
155
156
  if (scenarioCount < 1) {
@@ -171,7 +172,7 @@ export class Validator {
171
172
  issues.push({ level: 'ERROR', path: entryPath, message: `MODIFIED "${block.name}" is missing requirement text` });
172
173
  }
173
174
  else if (!this.containsShallOrMust(requirementText)) {
174
- issues.push({ level: 'ERROR', path: entryPath, message: `MODIFIED "${block.name}" must contain SHALL or MUST` });
175
+ issues.push({ level: 'ERROR', path: entryPath, message: this.buildMissingShallOrMustMessage('MODIFIED', block.name) });
175
176
  }
176
177
  const scenarioCount = this.countScenarios(block.raw);
177
178
  if (scenarioCount < 1) {
@@ -270,6 +271,14 @@ export class Validator {
270
271
  }
271
272
  applySpecRules(spec, content) {
272
273
  const issues = [];
274
+ for (const structuralIssue of findMainSpecStructureIssues(content)) {
275
+ issues.push({
276
+ level: 'ERROR',
277
+ path: 'file',
278
+ line: structuralIssue.line,
279
+ message: structuralIssue.message,
280
+ });
281
+ }
273
282
  if (spec.overview.length < MIN_PURPOSE_LENGTH) {
274
283
  issues.push({
275
284
  level: 'WARNING',
@@ -392,6 +401,23 @@ export class Validator {
392
401
  containsShallOrMust(text) {
393
402
  return /\b(SHALL|MUST)\b/.test(text);
394
403
  }
404
+ /**
405
+ * Build an error message for a requirement block whose body lacks SHALL/MUST.
406
+ *
407
+ * When the SHALL/MUST keyword already appears in the requirement header (e.g.
408
+ * `### Requirement: The system SHALL ...`) the original generic error
409
+ * ("must contain SHALL or MUST") is confusing because the keyword is visibly
410
+ * present in the spec. Per the OpenSpec conventions the keyword has to live
411
+ * on the requirement body line (the line right after the header), so we point
412
+ * the author at that exact fix when the keyword is found in the header only.
413
+ */
414
+ buildMissingShallOrMustMessage(action, blockName) {
415
+ const base = `${action} "${blockName}" must contain SHALL or MUST`;
416
+ if (this.containsShallOrMust(blockName)) {
417
+ return `${base} in the requirement body, not only in the header. Move the SHALL/MUST statement to the line immediately after the "### Requirement: ..." header.`;
418
+ }
419
+ return base;
420
+ }
395
421
  countScenarios(blockRaw) {
396
422
  const matches = blockRaw.match(/^####\s+/gm);
397
423
  return matches ? matches.length : 0;
@@ -0,0 +1,67 @@
1
+ import { type ContextStoreBinding, type ContextStoreSelector } from '../context-store/index.js';
2
+ export declare const WORKSPACE_METADATA_DIR_NAME = ".openspec-workspace";
3
+ export declare const WORKSPACE_VIEW_STATE_FILE_NAME = "workspace.yaml";
4
+ export declare const WORKSPACE_CHANGES_DIR_NAME = "changes";
5
+ export declare const WORKSPACE_CODE_WORKSPACE_EXTENSION = ".code-workspace";
6
+ export declare const WORKSPACE_SUPPORTED_OPENER_VALUES: readonly ["codex-cli", "claude", "github-copilot", "editor"];
7
+ export declare const WORKSPACE_AGENT_OPENER_IDS: readonly ["codex-cli", "claude", "github-copilot"];
8
+ export declare const WORKSPACE_EDITOR_OPENER_IDS: readonly ["vscode"];
9
+ export type WorkspaceSupportedOpenerValue = typeof WORKSPACE_SUPPORTED_OPENER_VALUES[number];
10
+ export type WorkspaceAgentOpenerId = typeof WORKSPACE_AGENT_OPENER_IDS[number];
11
+ export type WorkspaceEditorOpenerId = typeof WORKSPACE_EDITOR_OPENER_IDS[number];
12
+ export type WorkspacePreferredOpener = {
13
+ kind: 'agent';
14
+ id: WorkspaceAgentOpenerId;
15
+ } | {
16
+ kind: 'editor';
17
+ id: WorkspaceEditorOpenerId;
18
+ };
19
+ export interface WorkspaceContextState {
20
+ kind: 'initiative';
21
+ store: ContextStoreBinding;
22
+ initiative: {
23
+ id: string;
24
+ };
25
+ }
26
+ export interface WorkspaceViewState {
27
+ version: 1;
28
+ name: string;
29
+ context: WorkspaceContextState | null;
30
+ links: Record<string, string | null>;
31
+ preferred_opener?: WorkspacePreferredOpener;
32
+ tools?: string[];
33
+ workspace_skills?: WorkspaceSkillState;
34
+ }
35
+ export interface WorkspaceSkillState {
36
+ selected_agents: string[];
37
+ last_applied_profile?: 'core' | 'custom';
38
+ last_applied_delivery?: 'both' | 'skills' | 'commands';
39
+ last_applied_workflow_ids?: string[];
40
+ last_applied_at?: string;
41
+ }
42
+ export declare function getWorkspaceMetadataDir(workspaceRoot: string): string;
43
+ export declare function getWorkspaceViewStatePath(workspaceRoot: string): string;
44
+ export declare function getWorkspaceChangesDir(workspaceRoot: string): string;
45
+ export declare function getWorkspaceCodeWorkspaceFileName(workspaceName: string): string;
46
+ export declare function getWorkspaceCodeWorkspacePath(workspaceRoot: string, workspaceName: string): string;
47
+ /**
48
+ * @deprecated Managed workspaces no longer create portable ignore rules.
49
+ * This compatibility shim remains for callers that still ask which ignore
50
+ * patterns OpenSpec owns for workspace-local generated files.
51
+ */
52
+ export declare function getWorkspacePortableIgnorePatterns(_workspaceName?: string): string[];
53
+ export declare function validateWorkspaceName(name: string): string;
54
+ export declare function validateWorkspaceLinkName(name: string): string;
55
+ export declare function isValidWorkspaceName(name: string): boolean;
56
+ export declare function isValidWorkspaceLinkName(name: string): boolean;
57
+ export declare function isWorkspaceAgentOpenerId(value: string): value is WorkspaceAgentOpenerId;
58
+ export declare function isWorkspaceSupportedOpenerValue(value: string): value is WorkspaceSupportedOpenerValue;
59
+ export declare function parseWorkspacePreferredOpenerValue(value: string): WorkspacePreferredOpener;
60
+ export declare function validateWorkspacePreferredOpener(opener: WorkspacePreferredOpener): WorkspacePreferredOpener;
61
+ export declare function createWorkspaceInitiativeContext(store: ContextStoreBinding, initiativeId: string): WorkspaceContextState;
62
+ export declare function getWorkspaceContextStoreId(context: WorkspaceContextState): string;
63
+ export declare function getWorkspaceContextStoreSelector(context: WorkspaceContextState): ContextStoreSelector;
64
+ export declare function getWorkspaceContextInitiativeId(context: WorkspaceContextState): string;
65
+ export declare function parseWorkspaceViewState(content: string): WorkspaceViewState;
66
+ export declare function serializeWorkspaceViewState(state: WorkspaceViewState): string;
67
+ //# sourceMappingURL=foundation.d.ts.map