teamspec 3.2.0 → 4.1.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 (51) hide show
  1. package/README.md +24 -12
  2. package/bin/teamspec-init.js +2 -2
  3. package/lib/cli.js +653 -99
  4. package/lib/extension-installer.js +19 -219
  5. package/lib/linter.js +823 -1076
  6. package/lib/prompt-generator.js +312 -330
  7. package/lib/structure-loader.js +400 -0
  8. package/package.json +14 -6
  9. package/teamspec-core/FOLDER_STRUCTURE.yml +131 -0
  10. package/teamspec-core/agents/AGENT_BA.md +188 -293
  11. package/teamspec-core/agents/AGENT_BOOTSTRAP.md +197 -102
  12. package/teamspec-core/agents/AGENT_DES.md +9 -8
  13. package/teamspec-core/agents/AGENT_DEV.md +68 -67
  14. package/teamspec-core/agents/AGENT_FA.md +437 -245
  15. package/teamspec-core/agents/AGENT_FIX.md +344 -74
  16. package/teamspec-core/agents/AGENT_PO.md +487 -0
  17. package/teamspec-core/agents/AGENT_QA.md +124 -98
  18. package/teamspec-core/agents/AGENT_SA.md +143 -84
  19. package/teamspec-core/agents/AGENT_SM.md +106 -83
  20. package/teamspec-core/agents/README.md +143 -93
  21. package/teamspec-core/copilot-instructions.md +281 -205
  22. package/teamspec-core/definitions/definition-of-done.md +47 -84
  23. package/teamspec-core/definitions/definition-of-ready.md +35 -60
  24. package/teamspec-core/registry.yml +898 -0
  25. package/teamspec-core/teamspec.yml +44 -28
  26. package/teamspec-core/templates/README.md +5 -5
  27. package/teamspec-core/templates/adr-template.md +19 -17
  28. package/teamspec-core/templates/bai-template.md +125 -0
  29. package/teamspec-core/templates/bug-report-template.md +21 -15
  30. package/teamspec-core/templates/business-analysis-template.md +16 -13
  31. package/teamspec-core/templates/decision-log-template.md +26 -22
  32. package/teamspec-core/templates/dev-plan-template.md +168 -0
  33. package/teamspec-core/templates/epic-template.md +204 -0
  34. package/teamspec-core/templates/feature-increment-template.md +84 -0
  35. package/teamspec-core/templates/feature-template.md +45 -32
  36. package/teamspec-core/templates/increments-index-template.md +53 -0
  37. package/teamspec-core/templates/product-template.yml +44 -0
  38. package/teamspec-core/templates/products-index-template.md +46 -0
  39. package/teamspec-core/templates/project-template.yml +70 -0
  40. package/teamspec-core/templates/ri-template.md +225 -0
  41. package/teamspec-core/templates/rt-template.md +104 -0
  42. package/teamspec-core/templates/sd-template.md +132 -0
  43. package/teamspec-core/templates/sdi-template.md +119 -0
  44. package/teamspec-core/templates/sprint-template.md +17 -15
  45. package/teamspec-core/templates/story-template-v4.md +202 -0
  46. package/teamspec-core/templates/story-template.md +48 -90
  47. package/teamspec-core/templates/ta-template.md +198 -0
  48. package/teamspec-core/templates/tai-template.md +131 -0
  49. package/teamspec-core/templates/tc-template.md +145 -0
  50. package/teamspec-core/templates/testcases-template.md +20 -17
  51. package/extensions/teamspec-0.1.0.vsix +0 -0
@@ -0,0 +1,400 @@
1
+ /**
2
+ * TeamSpec Structure Loader
3
+ *
4
+ * Loads folder structure and registry definitions from YAML files.
5
+ * This module is the single source of truth for CLI structure generation.
6
+ *
7
+ * Version: 4.0
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const YAML = require('yaml');
13
+
14
+ /**
15
+ * Parse YAML content using the yaml library
16
+ */
17
+ function parseYaml(content) {
18
+ try {
19
+ return YAML.parse(content);
20
+ } catch (err) {
21
+ console.warn(`Warning: YAML parse error: ${err.message}`);
22
+ return {};
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Load the registry.yml from spec/4.0/
28
+ * Falls back to bundled version in cli/teamspec-core/ if not found
29
+ */
30
+ function loadRegistry(workspaceRoot) {
31
+ const specPath = path.join(workspaceRoot, 'spec', '4.0', 'registry.yml');
32
+ const bundledPath = path.join(__dirname, '..', 'teamspec-core', 'registry.yml');
33
+ const localBundled = path.join(__dirname, '..', '..', 'spec', '4.0', 'registry.yml');
34
+
35
+ let registryPath;
36
+ if (fs.existsSync(specPath)) {
37
+ registryPath = specPath;
38
+ } else if (fs.existsSync(bundledPath)) {
39
+ registryPath = bundledPath;
40
+ } else if (fs.existsSync(localBundled)) {
41
+ registryPath = localBundled;
42
+ } else {
43
+ return getDefaultRegistry();
44
+ }
45
+
46
+ try {
47
+ const content = fs.readFileSync(registryPath, 'utf-8');
48
+ return parseYaml(content);
49
+ } catch (err) {
50
+ console.warn(`Warning: Could not parse registry.yml: ${err.message}`);
51
+ return getDefaultRegistry();
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Load FOLDER_STRUCTURE.yml from spec/4.0/
57
+ */
58
+ function loadFolderStructure(workspaceRoot) {
59
+ const specPath = path.join(workspaceRoot, 'spec', '4.0', 'FOLDER_STRUCTURE.yml');
60
+ const bundledPath = path.join(__dirname, '..', 'teamspec-core', 'FOLDER_STRUCTURE.yml');
61
+ const localBundled = path.join(__dirname, '..', '..', 'spec', '4.0', 'FOLDER_STRUCTURE.yml');
62
+
63
+ let structurePath;
64
+ if (fs.existsSync(specPath)) {
65
+ structurePath = specPath;
66
+ } else if (fs.existsSync(bundledPath)) {
67
+ structurePath = bundledPath;
68
+ } else if (fs.existsSync(localBundled)) {
69
+ structurePath = localBundled;
70
+ } else {
71
+ return getDefaultFolderStructure();
72
+ }
73
+
74
+ try {
75
+ const content = fs.readFileSync(structurePath, 'utf-8');
76
+ return parseYaml(content);
77
+ } catch (err) {
78
+ console.warn(`Warning: Could not parse FOLDER_STRUCTURE.yml: ${err.message}`);
79
+ return getDefaultFolderStructure();
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Get default folder structure when YAML not available
85
+ */
86
+ function getDefaultFolderStructure() {
87
+ return {
88
+ structure: {
89
+ products: {
90
+ path: 'products/',
91
+ product_folder: {
92
+ path: 'products/{product-id}/',
93
+ subfolders: [
94
+ { path: 'business-analysis/' },
95
+ { path: 'features/' },
96
+ { path: 'solution-designs/' },
97
+ { path: 'technical-architecture/' },
98
+ { path: 'qa/regression-tests/' },
99
+ { path: 'decisions/' }
100
+ ]
101
+ }
102
+ },
103
+ projects: {
104
+ path: 'projects/',
105
+ project_folder: {
106
+ path: 'projects/{project-id}/',
107
+ subfolders: [
108
+ { path: 'business-analysis-increments/' },
109
+ { path: 'feature-increments/' },
110
+ { path: 'solution-design-increments/' },
111
+ { path: 'technical-architecture-increments/' },
112
+ { path: 'epics/' },
113
+ { path: 'stories/', workflow_subfolders: ['backlog/', 'ready-to-refine/', 'ready-to-develop/', 'deferred/', 'out-of-scope/'] },
114
+ { path: 'decisions/' },
115
+ { path: 'dev-plans/' },
116
+ { path: 'qa/', subfolders: ['test-cases/', 'bug-reports/', 'uat/', 'regression-impact/'] },
117
+ { path: 'sprints/' }
118
+ ]
119
+ }
120
+ }
121
+ },
122
+ story_workflow_folders: [
123
+ { folder: 'stories/backlog/', state: 'backlog' },
124
+ { folder: 'stories/ready-to-refine/', state: 'ready-to-refine' },
125
+ { folder: 'stories/ready-to-develop/', state: 'ready-to-develop' },
126
+ { folder: 'sprints/sprint-N/', state: 'in-sprint' },
127
+ { folder: 'stories/deferred/', state: 'deferred', terminal: true },
128
+ { folder: 'stories/out-of-scope/', state: 'out-of-scope', terminal: true }
129
+ ],
130
+ canon_sync_paths: [
131
+ { from: 'projects/{project-id}/feature-increments/', to: 'products/{product-id}/features/' },
132
+ { from: 'projects/{project-id}/business-analysis-increments/', to: 'products/{product-id}/business-analysis/' },
133
+ { from: 'projects/{project-id}/solution-design-increments/', to: 'products/{product-id}/solution-designs/' },
134
+ { from: 'projects/{project-id}/technical-architecture-increments/', to: 'products/{product-id}/technical-architecture/' }
135
+ ]
136
+ };
137
+ }
138
+
139
+ /**
140
+ * Get default registry when YAML not available
141
+ */
142
+ function getDefaultRegistry() {
143
+ return {
144
+ version: '4.0',
145
+ model: 'Product-Canon',
146
+ roles: {
147
+ PO: { name: 'Product Owner', commands: ['ts:po product', 'ts:po project', 'ts:po sync', 'ts:po status'] },
148
+ BA: { name: 'Business Analyst', commands: ['ts:ba analysis', 'ts:ba ba-increment', 'ts:ba review'] },
149
+ FA: { name: 'Functional Analyst', commands: ['ts:fa feature', 'ts:fa feature-increment', 'ts:fa epic', 'ts:fa story', 'ts:fa sync-proposal', 'ts:fa slice'] },
150
+ SA: { name: 'Solution Architect', commands: ['ts:sa ta', 'ts:sa ta-increment', 'ts:sa sd', 'ts:sa sd-increment', 'ts:sa review'] },
151
+ DEV: { name: 'Developer', commands: ['ts:dev plan', 'ts:dev implement'] },
152
+ QA: { name: 'QA Engineer', commands: ['ts:qa test', 'ts:qa verify', 'ts:qa regression', 'ts:qa bug', 'ts:qa uat'] },
153
+ SM: { name: 'Scrum Master', commands: ['ts:sm sprint', 'ts:sm deploy-checklist', 'ts:sm planning', 'ts:sm standup', 'ts:sm retro', 'ts:sm sync'] }
154
+ },
155
+ artifacts: {
156
+ feature: { location: 'products/{product-id}/features/', naming: 'f-{PRX}-{NNN}-{description}.md', owner: 'FA' },
157
+ 'feature-increment': { location: 'projects/{project-id}/feature-increments/', naming: 'fi-{PRX}-{NNN}-{description}.md', owner: 'FA' },
158
+ epic: { location: 'projects/{project-id}/epics/', naming: 'epic-{PRX}-{NNN}-{description}.md', owner: 'FA' },
159
+ story: { location: 'projects/{project-id}/stories/{state}/', naming: 's-e{EEE}-{SSS}-{description}.md', owner: 'FA' },
160
+ 'dev-plan': { location: 'projects/{project-id}/dev-plans/', naming: 'dp-e{EEE}-s{SSS}-{description}.md', owner: 'DEV' },
161
+ 'project-test-case': { location: 'projects/{project-id}/qa/test-cases/', naming: 'tc-fi-{PRX}-{NNN}-{description}.md', owner: 'QA' },
162
+ 'product-regression-test': { location: 'products/{product-id}/qa/regression-tests/', naming: 'rt-f-{PRX}-{NNN}-{description}.md', owner: 'QA' },
163
+ 'regression-impact': { location: 'projects/{project-id}/qa/regression-impact/', naming: 'ri-fi-{PRX}-{NNN}.md', owner: 'QA' },
164
+ 'bug-report': { location: 'projects/{project-id}/qa/bug-reports/', naming: 'bug-{project-id}-{NNN}-{description}.md', owner: 'QA' }
165
+ },
166
+ commands: [],
167
+ gates: {
168
+ dor: { name: 'Definition of Ready', checks: [] },
169
+ dod: { name: 'Definition of Done', checks: [] },
170
+ deployment: { name: 'Deployment Verification Gate', checks: [] }
171
+ }
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Extract all commands from registry for prompt generation
177
+ */
178
+ function getCommandsFromRegistry(registry) {
179
+ const commands = [];
180
+
181
+ // Extract commands array if it exists
182
+ if (Array.isArray(registry.commands)) {
183
+ for (const cmd of registry.commands) {
184
+ if (cmd.status === 'REMOVED') continue;
185
+ if (!cmd.invocation) continue;
186
+
187
+ commands.push({
188
+ id: cmd.id,
189
+ invocation: cmd.invocation,
190
+ role: cmd.role,
191
+ purpose: cmd.purpose,
192
+ output: cmd.output
193
+ });
194
+ }
195
+ }
196
+
197
+ // Also extract from roles.*.commands
198
+ if (registry.roles) {
199
+ for (const [roleKey, roleData] of Object.entries(registry.roles)) {
200
+ if (Array.isArray(roleData.commands)) {
201
+ for (const cmdInvocation of roleData.commands) {
202
+ // Check if already in commands array
203
+ const exists = commands.some(c => c.invocation === cmdInvocation);
204
+ if (!exists) {
205
+ commands.push({
206
+ id: cmdInvocation.replace('ts:', '').replace(' ', '.'),
207
+ invocation: cmdInvocation,
208
+ role: roleKey,
209
+ purpose: `${roleData.name} command`
210
+ });
211
+ }
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ return commands;
218
+ }
219
+
220
+ /**
221
+ * Get artifact naming patterns from registry
222
+ */
223
+ function getArtifactPatterns(registry) {
224
+ const patterns = {};
225
+
226
+ if (registry.artifacts) {
227
+ for (const [artifactKey, artifactData] of Object.entries(registry.artifacts)) {
228
+ patterns[artifactKey] = {
229
+ location: artifactData.location,
230
+ naming: artifactData.naming,
231
+ owner: artifactData.owner,
232
+ example: artifactData.example
233
+ };
234
+ }
235
+ }
236
+
237
+ return patterns;
238
+ }
239
+
240
+ /**
241
+ * Get product folders from structure
242
+ */
243
+ function getProductFolders(structure) {
244
+ const folders = [];
245
+
246
+ if (structure?.structure?.products?.product_folder?.subfolders) {
247
+ for (const subfolder of structure.structure.products.product_folder.subfolders) {
248
+ if (subfolder.path) {
249
+ folders.push(subfolder.path.replace(/\/$/, ''));
250
+ }
251
+ }
252
+ }
253
+
254
+ return folders.length > 0 ? folders : [
255
+ 'business-analysis',
256
+ 'features',
257
+ 'solution-designs',
258
+ 'technical-architecture',
259
+ 'qa/regression-tests',
260
+ 'decisions'
261
+ ];
262
+ }
263
+
264
+ /**
265
+ * Get project folders from structure
266
+ */
267
+ function getProjectFolders(structure) {
268
+ const folders = [];
269
+
270
+ if (structure?.structure?.projects?.project_folder?.subfolders) {
271
+ for (const subfolder of structure.structure.projects.project_folder.subfolders) {
272
+ if (subfolder.path) {
273
+ const basePath = subfolder.path.replace(/\/$/, '');
274
+ folders.push(basePath);
275
+
276
+ // Add nested workflow subfolders
277
+ if (subfolder.workflow_subfolders) {
278
+ for (const wf of subfolder.workflow_subfolders) {
279
+ folders.push(`${basePath}/${wf.replace(/\/$/, '')}`);
280
+ }
281
+ }
282
+
283
+ // Add nested subfolders
284
+ if (subfolder.subfolders) {
285
+ for (const sf of subfolder.subfolders) {
286
+ folders.push(`${basePath}/${sf.replace(/\/$/, '')}`);
287
+ }
288
+ }
289
+ }
290
+ }
291
+ }
292
+
293
+ return folders.length > 0 ? folders : [
294
+ 'business-analysis-increments',
295
+ 'feature-increments',
296
+ 'solution-design-increments',
297
+ 'technical-architecture-increments',
298
+ 'epics',
299
+ 'stories',
300
+ 'stories/backlog',
301
+ 'stories/ready-to-refine',
302
+ 'stories/ready-to-develop',
303
+ 'stories/deferred',
304
+ 'stories/out-of-scope',
305
+ 'decisions',
306
+ 'dev-plans',
307
+ 'qa',
308
+ 'qa/test-cases',
309
+ 'qa/bug-reports',
310
+ 'qa/uat',
311
+ 'qa/regression-impact',
312
+ 'sprints'
313
+ ];
314
+ }
315
+
316
+ /**
317
+ * Get story workflow folders
318
+ */
319
+ function getStoryWorkflowFolders(structure) {
320
+ if (structure?.story_workflow_folders) {
321
+ return structure.story_workflow_folders;
322
+ }
323
+
324
+ return [
325
+ { folder: 'stories/backlog/', state: 'backlog' },
326
+ { folder: 'stories/ready-to-refine/', state: 'ready-to-refine' },
327
+ { folder: 'stories/ready-to-develop/', state: 'ready-to-develop' },
328
+ { folder: 'sprints/sprint-N/', state: 'in-sprint' }
329
+ ];
330
+ }
331
+
332
+ /**
333
+ * Convert naming pattern from registry.yml to regex
334
+ *
335
+ * Converts patterns like:
336
+ * - "f-{PRX}-{NNN}-{description}.md" -> /^f-[A-Z]{3,4}-\d{3}-[\w-]+\.md$/
337
+ * - "s-e{EEE}-{SSS}-{description}.md" -> /^s-e\d{3}-\d{3}-[\w-]+\.md$/
338
+ *
339
+ * @param {string} pattern - Naming pattern from registry
340
+ * @returns {RegExp} - Compiled regex pattern
341
+ */
342
+ function namingPatternToRegex(pattern) {
343
+ if (!pattern) return null;
344
+
345
+ // Escape special regex characters first (except { and })
346
+ let regexStr = pattern
347
+ .replace(/\./g, '\\.')
348
+ .replace(/\*/g, '.*');
349
+
350
+ // Replace placeholders with regex patterns
351
+ regexStr = regexStr
352
+ .replace(/\{PRX\}/g, '[A-Z]{3,4}') // Product prefix
353
+ .replace(/\{NNN\}/g, '\\d{3}') // 3-digit number
354
+ .replace(/\{EEE\}/g, '\\d{3}') // Epic number
355
+ .replace(/\{SSS\}/g, '\\d{3}') // Story sequence
356
+ .replace(/\{N\}/g, '\\d+') // Sprint number
357
+ .replace(/\{description\}/g, '[\\w-]+') // Description slug
358
+ .replace(/\{product-id\}/g, '[\\w-]+') // Product ID
359
+ .replace(/\{project-id\}/g, '[\\w-]+') // Project ID
360
+ .replace(/\{state\}/g, '[\\w-]+'); // Story state
361
+
362
+ return new RegExp(`^${regexStr}$`);
363
+ }
364
+
365
+ /**
366
+ * Get naming patterns as regex from registry artifacts
367
+ * @param {Object} registry - Parsed registry
368
+ * @returns {Object} - Map of artifact type to regex pattern
369
+ */
370
+ function getArtifactNamingRegex(registry) {
371
+ const patterns = {};
372
+
373
+ if (registry?.artifacts) {
374
+ for (const [artifactKey, artifactData] of Object.entries(registry.artifacts)) {
375
+ if (artifactData.naming) {
376
+ const regex = namingPatternToRegex(artifactData.naming);
377
+ if (regex) {
378
+ patterns[artifactKey] = regex;
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ return patterns;
385
+ }
386
+
387
+ module.exports = {
388
+ parseYaml,
389
+ loadRegistry,
390
+ loadFolderStructure,
391
+ getCommandsFromRegistry,
392
+ getArtifactPatterns,
393
+ getArtifactNamingRegex,
394
+ namingPatternToRegex,
395
+ getProductFolders,
396
+ getProjectFolders,
397
+ getStoryWorkflowFolders,
398
+ getDefaultRegistry,
399
+ getDefaultFolderStructure
400
+ };
package/package.json CHANGED
@@ -1,20 +1,25 @@
1
1
  {
2
2
  "name": "teamspec",
3
- "version": "3.2.0",
4
- "description": "CLI tool to bootstrap TeamSpec 2.0 Feature Canon operating model in any repository",
3
+ "version": "4.1.0",
4
+ "description": "CLI tool to bootstrap TeamSpec 4.0 Product-Canon operating model in any repository",
5
5
  "main": "lib/cli.js",
6
6
  "bin": {
7
- "teamspec": "./bin/teamspec-init.js"
7
+ "teamspec": "bin/teamspec-init.js"
8
8
  },
9
9
  "scripts": {
10
+ "sync": "node ../scripts/sync-cli-core.js",
10
11
  "test": "node --test test/**/*.test.js",
11
12
  "lint": "eslint lib/**/*.js",
12
- "prepublishOnly": "npm test"
13
+ "check:parity": "node ../scripts/check-parity.js",
14
+ "check:consistency": "node ../scripts/check-consistency.js",
15
+ "pretest": "npm run check:consistency",
16
+ "prepublishOnly": "npm run sync && npm test"
13
17
  },
14
18
  "keywords": [
15
19
  "teamspec",
16
20
  "agile",
17
21
  "specifications",
22
+ "product-canon",
18
23
  "feature-canon",
19
24
  "copilot",
20
25
  "cursor",
@@ -47,5 +52,8 @@
47
52
  "README.md",
48
53
  "LICENSE"
49
54
  ],
50
- "preferGlobal": true
51
- }
55
+ "preferGlobal": true,
56
+ "dependencies": {
57
+ "yaml": "^2.8.2"
58
+ }
59
+ }
@@ -0,0 +1,131 @@
1
+ # TeamSpec 4.0 Folder Structure Encoding
2
+ # Encodes the physical folder hierarchy for AI prompts and tooling
3
+ # Version: 4.0
4
+ # Last Updated: 2026-01-11
5
+ #
6
+ # NOTE: Role ownership, artifact naming, and gates are defined in registry.yml
7
+ # This file focuses ONLY on folder paths and hierarchy
8
+
9
+ # =============================================================================
10
+ # CORE CONCEPT: Products vs Projects
11
+ # =============================================================================
12
+ # Products = Production truth (AS-IS state) - canonical, permanent
13
+ # Projects = Change proposals (TO-BE state) - time-bound, sync to product on deploy
14
+
15
+ structure:
16
+ # ---------------------------------------------------------------------------
17
+ # PRODUCTS - Production Truth
18
+ # ---------------------------------------------------------------------------
19
+ products:
20
+ path: "products/"
21
+
22
+ product_folder:
23
+ path: "products/{product-id}/"
24
+ files:
25
+ - "product.yml"
26
+ - "README.md"
27
+
28
+ subfolders:
29
+ - path: "business-analysis/"
30
+ - path: "features/"
31
+ key_files:
32
+ - "features-index.md"
33
+ - "story-ledger.md"
34
+ - path: "solution-designs/"
35
+ - path: "technical-architecture/"
36
+ - path: "qa/regression-tests/"
37
+ - path: "decisions/"
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # PROJECTS - Change Proposals
41
+ # ---------------------------------------------------------------------------
42
+ projects:
43
+ path: "projects/"
44
+
45
+ project_folder:
46
+ path: "projects/{project-id}/"
47
+ files:
48
+ - "project.yml"
49
+ - "README.md"
50
+
51
+ subfolders:
52
+ - path: "business-analysis-increments/"
53
+ - path: "feature-increments/"
54
+ key_files:
55
+ - "increments-index.md"
56
+ - path: "solution-design-increments/"
57
+ - path: "technical-architecture-increments/"
58
+ - path: "epics/"
59
+ key_files:
60
+ - "epics-index.md"
61
+ - path: "stories/"
62
+ workflow_subfolders:
63
+ - "backlog/"
64
+ - "ready-to-refine/"
65
+ - "ready-to-develop/"
66
+ - "deferred/"
67
+ - "out-of-scope/"
68
+ - path: "decisions/"
69
+ - path: "dev-plans/"
70
+ - path: "qa/"
71
+ subfolders:
72
+ - "test-cases/"
73
+ - "bug-reports/"
74
+ - "uat/"
75
+ - "regression-impact/"
76
+ - path: "sprints/"
77
+ key_files:
78
+ - "active-sprint.md"
79
+ sprint_folder:
80
+ pattern: "sprint-N/"
81
+ files:
82
+ - "sprint-goal.md"
83
+ - "committed-stories.md"
84
+ - "retrospective.md"
85
+
86
+ # =============================================================================
87
+ # STORY WORKFLOW FOLDERS
88
+ # =============================================================================
89
+ # Stories move between folders based on workflow state
90
+ story_workflow_folders:
91
+ - folder: "stories/backlog/"
92
+ state: "backlog"
93
+ next: "stories/ready-to-refine/"
94
+
95
+ - folder: "stories/ready-to-refine/"
96
+ state: "ready-to-refine"
97
+ next: "stories/ready-to-develop/"
98
+
99
+ - folder: "stories/ready-to-develop/"
100
+ state: "ready-to-develop"
101
+ next: "sprints/sprint-N/"
102
+
103
+ - folder: "sprints/sprint-N/"
104
+ state: "in-sprint"
105
+ next: "completed (archived)"
106
+
107
+ # Terminal states
108
+ - folder: "stories/deferred/"
109
+ state: "deferred"
110
+ terminal: true
111
+
112
+ - folder: "stories/out-of-scope/"
113
+ state: "out-of-scope"
114
+ terminal: true
115
+
116
+ # =============================================================================
117
+ # CANON SYNC FOLDER MAPPINGS
118
+ # =============================================================================
119
+ # Post-deployment, project artifacts sync to product folders
120
+ canon_sync_paths:
121
+ - from: "projects/{project-id}/feature-increments/"
122
+ to: "products/{product-id}/features/"
123
+
124
+ - from: "projects/{project-id}/business-analysis-increments/"
125
+ to: "products/{product-id}/business-analysis/"
126
+
127
+ - from: "projects/{project-id}/solution-design-increments/"
128
+ to: "products/{product-id}/solution-designs/"
129
+
130
+ - from: "projects/{project-id}/technical-architecture-increments/"
131
+ to: "products/{product-id}/technical-architecture/"