@sppg2001/atomize 1.2.2 → 2.0.0-alpha.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.
@@ -1,6 +1,6 @@
1
1
  # Examples
2
2
 
3
- Real-world Atomize template examples. Each file is a working template you can validate and run immediately.
3
+ Real-world Atomize template examples. Each file is a working template you can validate immediately and dry-run against either matching work items or explicit mock stories.
4
4
 
5
5
  ## Files
6
6
 
@@ -32,7 +32,7 @@ A production-ready backend API template. Covers the full development cycle from
32
32
  **Try it:**
33
33
  ```bash
34
34
  atomize validate examples/backend.yaml
35
- atomize generate examples/backend.yaml --platform mock --dry-run
35
+ atomize generate examples/backend.yaml --platform mock
36
36
  ```
37
37
 
38
38
  ---
@@ -54,7 +54,7 @@ A React component development template covering the full UI lifecycle including
54
54
  **Try it:**
55
55
  ```bash
56
56
  atomize validate examples/frontend.yaml
57
- atomize generate examples/frontend.yaml --platform mock --dry-run
57
+ atomize generate examples/frontend.yaml --platform mock
58
58
  ```
59
59
 
60
60
  ---
@@ -79,7 +79,7 @@ A combined backend + frontend template for end-to-end features. Uses a branching
79
79
  **Try it:**
80
80
  ```bash
81
81
  atomize validate examples/fullstack.yaml
82
- atomize generate examples/fullstack.yaml --platform mock --dry-run
82
+ atomize generate examples/fullstack.yaml --platform mock --story STORY-004
83
83
  ```
84
84
 
85
85
  ---
@@ -114,7 +114,7 @@ Demonstrates **conditional tasks** — tasks that are only created when a story
114
114
  **Try it:**
115
115
  ```bash
116
116
  atomize validate examples/conditional-dependencies-template.yaml
117
- atomize generate examples/conditional-dependencies-template.yaml --platform mock --dry-run
117
+ atomize generate examples/conditional-dependencies-template.yaml --platform mock --story STORY-004
118
118
  ```
119
119
 
120
120
  ---
@@ -155,7 +155,7 @@ All totals are normalized to exactly 100% at generation time.
155
155
  **Try it:**
156
156
  ```bash
157
157
  atomize validate examples/conditional-percentage-template.yaml
158
- atomize generate examples/conditional-percentage-template.yaml --platform mock --dry-run
158
+ atomize generate examples/conditional-percentage-template.yaml --platform mock
159
159
  ```
160
160
 
161
161
  ---
@@ -168,7 +168,7 @@ A reference template that demonstrates every available filter option. Use it as
168
168
 
169
169
  | Filter | What it does |
170
170
  |--------|-------------|
171
- | `team` | Overrides the `AZURE_DEVOPS_TEAM` env var for this template |
171
+ | `team` | Overrides the team from the selected Azure DevOps profile for this template |
172
172
  | `statesExclude` | Excludes items in `Done`, `Removed`, or `Won't Fix` |
173
173
  | `statesWereEver` | Matches items that passed through `In Review` historically |
174
174
  | `areaPathsUnder` | Matches `MyProject\Backend` **and all sub-areas** (hierarchy traversal) |
@@ -185,7 +185,7 @@ A reference template that demonstrates every available filter option. Use it as
185
185
  **Try it:**
186
186
  ```bash
187
187
  atomize validate examples/advanced-filtering.yaml
188
- atomize generate examples/advanced-filtering.yaml --platform mock --dry-run
188
+ atomize generate examples/advanced-filtering.yaml --platform mock --story STORY-001
189
189
  ```
190
190
 
191
191
  ---
@@ -199,11 +199,16 @@ for f in examples/*.yaml; do
199
199
  atomize validate "$f"
200
200
  done
201
201
 
202
- # Test all examples against mock data
203
- for f in examples/*.yaml; do
202
+ # Dry-run examples that match the bundled mock filters
203
+ for f in examples/backend.yaml examples/frontend.yaml examples/conditional-percentage-template.yaml; do
204
204
  echo "Testing $f..."
205
- atomize generate "$f" --platform mock --dry-run
205
+ atomize generate "$f" --platform mock
206
206
  done
207
+
208
+ # Dry-run examples whose filters are intentionally more specific than the mock dataset
209
+ atomize generate examples/fullstack.yaml --platform mock --story STORY-004
210
+ atomize generate examples/conditional-dependencies-template.yaml --platform mock --story STORY-004
211
+ atomize generate examples/advanced-filtering.yaml --platform mock --story STORY-001
207
212
  ```
208
213
 
209
214
  ---
@@ -10,7 +10,7 @@ tags: ["demo", "filtering", "advanced"]
10
10
  # Each option below is independent — in a real template you would only use
11
11
  # the subset that makes sense for your workflow.
12
12
  filter:
13
- # Override the team configured in AZURE_DEVOPS_TEAM env var
13
+ # Override the team from the selected Azure DevOps profile
14
14
  team: "Backend Team"
15
15
 
16
16
  workItemTypes:
@@ -31,7 +31,10 @@ tasks:
31
31
  estimationPercent: 30
32
32
  activity: "Development"
33
33
  dependsOn: ["design"]
34
- condition: '${story.tags} CONTAINS "backend"'
34
+ condition:
35
+ field: tags
36
+ operator: contains
37
+ value: backend
35
38
  tags:
36
39
  - "backend"
37
40
  - "api"
@@ -43,7 +46,10 @@ tasks:
43
46
  estimationPercent: 35
44
47
  activity: "Development"
45
48
  dependsOn: ["design"]
46
- condition: '${story.tags} CONTAINS "frontend"'
49
+ condition:
50
+ field: tags
51
+ operator: contains
52
+ value: frontend
47
53
  tags:
48
54
  - "frontend"
49
55
 
@@ -54,7 +60,14 @@ tasks:
54
60
  estimationPercent: 7
55
61
  activity: "Testing"
56
62
  dependsOn: ["frontend-ui"]
57
- condition: '${story.tags} CONTAINS "frontend" OR ${story.tags} NOT CONTAINS "legacy"'
63
+ condition:
64
+ any:
65
+ - field: tags
66
+ operator: contains
67
+ value: frontend
68
+ - field: tags
69
+ operator: not-contains
70
+ value: legacy
58
71
  tags:
59
72
  - "accessibility"
60
73
  - "testing"
@@ -66,7 +79,14 @@ tasks:
66
79
  estimationPercent: 8
67
80
  activity: "Testing"
68
81
  dependsOn: ["backend-api", "frontend-ui"]
69
- condition: '${story.tags} CONTAINS "security" AND ${story.priority} <= 2'
82
+ condition:
83
+ all:
84
+ - field: tags
85
+ operator: contains
86
+ value: security
87
+ - field: priority
88
+ operator: lte
89
+ value: 2
70
90
  priority: 1
71
91
  tags:
72
92
  - "security"
@@ -97,7 +117,10 @@ tasks:
97
117
  estimationPercent: 10
98
118
  activity: "Testing"
99
119
  dependsOn: ["integration-tests"]
100
- condition: '${story.estimation} >= 13'
120
+ condition:
121
+ field: estimation
122
+ operator: gte
123
+ value: 13
101
124
  tags:
102
125
  - "performance"
103
126
 
@@ -31,9 +31,15 @@ tasks:
31
31
  assignTo: "@ParentAssignee"
32
32
  estimationPercent: 10
33
33
  estimationPercentCondition:
34
- - condition: "${story.estimation} >= 13"
34
+ - condition:
35
+ field: estimation
36
+ operator: gte
37
+ value: 13
35
38
  percent: 20
36
- - condition: "${story.estimation} >= 5"
39
+ - condition:
40
+ field: estimation
41
+ operator: gte
42
+ value: 5
37
43
  percent: 15
38
44
  tags: ["design"]
39
45
 
@@ -51,9 +57,15 @@ tasks:
51
57
  dependsOn: ["design"]
52
58
  estimationPercent: 60
53
59
  estimationPercentCondition:
54
- - condition: '${story.tags} CONTAINS "fullstack"'
60
+ - condition:
61
+ field: tags
62
+ operator: contains
63
+ value: fullstack
55
64
  percent: 40
56
- - condition: "${story.estimation} >= 5"
65
+ - condition:
66
+ field: estimation
67
+ operator: gte
68
+ value: 5
57
69
  percent: 50
58
70
  tags: ["backend"]
59
71
 
@@ -70,10 +82,16 @@ tasks:
70
82
  activity: "Development"
71
83
  assignTo: "@ParentAssignee"
72
84
  dependsOn: ["design"]
73
- condition: '${story.tags} CONTAINS "fullstack"'
85
+ condition:
86
+ field: tags
87
+ operator: contains
88
+ value: fullstack
74
89
  estimationPercent: 30
75
90
  estimationPercentCondition:
76
- - condition: '${story.tags} CONTAINS "complex-ui"'
91
+ - condition:
92
+ field: tags
93
+ operator: contains
94
+ value: complex-ui
77
95
  percent: 40
78
96
  tags: ["frontend"]
79
97
 
@@ -90,9 +108,19 @@ tasks:
90
108
  dependsOn: ["backend-impl"]
91
109
  estimationPercent: 20
92
110
  estimationPercentCondition:
93
- - condition: '${story.tags} CONTAINS "critical" AND ${story.estimation} >= 8'
111
+ - condition:
112
+ all:
113
+ - field: tags
114
+ operator: contains
115
+ value: critical
116
+ - field: estimation
117
+ operator: gte
118
+ value: 8
94
119
  percent: 30
95
- - condition: '${story.tags} CONTAINS "critical"'
120
+ - condition:
121
+ field: tags
122
+ operator: contains
123
+ value: critical
96
124
  percent: 25
97
125
  tags: ["testing"]
98
126
 
@@ -120,13 +148,13 @@ metadata:
120
148
 
121
149
  Scenario A — small backend-only story (< 5 pts, no "fullstack" tag):
122
150
  design=10 backend=60 testing=20 review=5 → total=95 → normalised to 100
123
- Scenario B — medium backend-only story (5–12 pts):
151
+ Scenario B — medium backend-only story (5/12 pts):
124
152
  design=15 backend=50 testing=20 review=5 → total=90 → normalised to 100
125
- Scenario C — medium fullstack story (5–12 pts, "fullstack" tag):
153
+ Scenario C — medium fullstack story (5/12 pts, "fullstack" tag):
126
154
  design=15 backend=40 frontend=30 testing=20 review=5 → total=110 → normalised
127
155
  Scenario D — large critical fullstack story (≥ 13 pts, "critical" + "fullstack" tags):
128
156
  design=20 backend=40 frontend=30 testing=30 review=5 → total=125 → normalised
129
- Scenario E — medium critical backend-only story (5–12 pts, "critical", no "fullstack"):
157
+ Scenario E — medium critical backend-only story (5/12 pts, "critical", no "fullstack"):
130
158
  design=15 backend=50 testing=25 review=5 → total=95 → normalised to 100
131
159
 
132
160
  Key behaviour: when frontend-impl is skipped (no "fullstack" tag) the
@@ -54,7 +54,10 @@ tasks:
54
54
  estimationPercent: 15
55
55
  tags: ["database", "backend"]
56
56
  activity: "Development"
57
- condition: '${story.tags} CONTAINS "database"'
57
+ condition:
58
+ field: tags
59
+ operator: contains
60
+ value: database
58
61
 
59
62
  - id: "backend-tests"
60
63
  title: "Backend Tests"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sppg2001/atomize",
3
- "version": "1.2.2",
3
+ "version": "2.0.0-alpha.0",
4
4
  "description": "Automatically generate tasks from user stories with smart templates",
5
5
  "type": "module",
6
6
  "main": "./dist/cli/index.js",
@@ -10,11 +10,13 @@
10
10
  },
11
11
  "publishConfig": {
12
12
  "access": "public",
13
- "tag": "latest"
13
+ "tag": "alpha"
14
14
  },
15
15
  "files": [
16
16
  "dist",
17
- "templates/presets",
17
+ "templates/templates",
18
+ "templates/mixins",
19
+ "docs",
18
20
  "README.md",
19
21
  "LICENSE",
20
22
  ".env.example",
@@ -33,6 +35,7 @@
33
35
  "test:watch": "bun test --watch",
34
36
  "test:coverage": "bun test --coverage --coverage-reporter=lcov",
35
37
  "typecheck": "tsc --noEmit",
38
+ "audit": "bun audit --audit-level=moderate",
36
39
  "validate": "bun run src/cli/index.ts validate",
37
40
  "validate:package": "node ./scripts/validate-package.mjs",
38
41
  "check": "bun typecheck && bun test",
@@ -62,27 +65,33 @@
62
65
  "author": "Simão Pereira Gomes",
63
66
  "license": "MIT",
64
67
  "devDependencies": {
65
- "@biomejs/biome": "2.3.11",
66
- "@types/bun": "1.3.5",
68
+ "@biomejs/biome": "2.4.12",
69
+ "@types/bun": "1.3.13",
67
70
  "@types/keytar": "^4.4.2",
68
- "@types/node": "^25.0.3",
69
- "@types/update-notifier": "^6.0.8",
70
- "mitata": "^1.0.34"
71
+ "@types/node": "^25.6.0",
72
+ "@types/semver": "^7.7.1",
73
+ "bun-types": "^1.3.13",
74
+ "mitata": "^1.0.34",
75
+ "typescript": "^6.0.3"
71
76
  },
72
77
  "peerDependencies": {
73
- "typescript": "^5.9.3"
78
+ "typescript": "^6.0.3"
74
79
  },
75
80
  "dependencies": {
76
- "@clack/prompts": "^1.1.0",
81
+ "@clack/prompts": "^1.2.0",
77
82
  "azure-devops-node-api": "^15.1.2",
78
83
  "chalk": "^5.6.2",
79
- "commander": "^14.0.2",
84
+ "commander": "^14.0.3",
80
85
  "keytar": "^7.9.0",
86
+ "openai": "^6.34.0",
87
+ "semver": "^7.7.4",
81
88
  "ts-pattern": "^5.9.0",
82
- "update-notifier": "^7.3.1",
83
- "winston": "^3.19.0",
84
- "yaml": "^2.8.2",
85
- "zod": "^4.3.5"
89
+ "yaml": "^2.8.3",
90
+ "zod": "^4.3.6"
91
+ },
92
+ "overrides": {
93
+ "qs": "6.15.1",
94
+ "underscore": "1.13.8"
86
95
  },
87
96
  "engines": {
88
97
  "node": ">=18.0.0"
@@ -0,0 +1,16 @@
1
+ name: "Documentation Mixin"
2
+ description: "Standard documentation tasks. Mix into any template to ensure docs are part of the definition of done."
3
+ tasks:
4
+ - id: "update-docs"
5
+ title: "Update Documentation: ${story.title}"
6
+ description: "Update or create user-facing documentation, API docs, and README files to reflect the changes."
7
+ estimationPercent: 10
8
+ activity: "Documentation"
9
+ assignTo: "@Unassigned"
10
+
11
+ - id: "changelog-entry"
12
+ title: "Add Changelog Entry: ${story.title}"
13
+ description: "Add a clear, user-focused entry to the project changelog describing what changed and why."
14
+ estimationPercent: 5
15
+ activity: "Documentation"
16
+ assignTo: "@ParentAssignee"
@@ -0,0 +1,16 @@
1
+ name: "Security Mixin"
2
+ description: "Common security review tasks. Mix into any feature template to enforce a security gate."
3
+ tasks:
4
+ - id: "security-review"
5
+ title: "Security Review: ${story.title}"
6
+ description: "Review the implementation for security vulnerabilities (OWASP Top 10, input validation, auth/authz, secrets handling)."
7
+ estimationPercent: 10
8
+ activity: "Design"
9
+ assignTo: "@Unassigned"
10
+
11
+ - id: "dependency-audit"
12
+ title: "Dependency Audit: ${story.title}"
13
+ description: "Audit third-party dependencies for known vulnerabilities (e.g. npm audit, OWASP Dependency-Check)."
14
+ estimationPercent: 5
15
+ activity: "Development"
16
+ assignTo: "@Unassigned"
@@ -0,0 +1,27 @@
1
+ name: "Testing Mixin"
2
+ description: "Expanded testing tasks. Mix into any template to enforce unit, integration, and E2E test coverage."
3
+ tasks:
4
+ - id: "unit-tests"
5
+ title: "Unit Tests: ${story.title}"
6
+ description: "Write unit tests covering the core logic and edge cases. Aim for ≥80% coverage on changed code."
7
+ estimationPercent: 15
8
+ activity: "Testing"
9
+ assignTo: "@ParentAssignee"
10
+
11
+ - id: "integration-tests"
12
+ title: "Integration Tests: ${story.title}"
13
+ description: "Write integration tests covering interactions between components, APIs, and external dependencies."
14
+ estimationPercent: 10
15
+ activity: "Testing"
16
+ dependsOn: ["unit-tests"]
17
+
18
+ - id: "e2e-tests"
19
+ title: "End-to-End Tests: ${story.title}"
20
+ description: "Validate the full user journey through the feature using E2E test scenarios."
21
+ estimationPercent: 10
22
+ activity: "Testing"
23
+ assignTo: "@Unassigned"
24
+ condition:
25
+ field: "tags"
26
+ operator: "contains"
27
+ value: "e2e"
@@ -0,0 +1,54 @@
1
+ version: "1.0"
2
+ name: "Bug Template"
3
+ description: "Foundation for bug-fix templates. Extend this to inherit a standard investigation and fix task set."
4
+ author: "Atomize"
5
+ tags: ["template", "bug"]
6
+
7
+ filter:
8
+ workItemTypes: ["Bug"]
9
+ states: ["New", "Active", "Committed"]
10
+ excludeIfHasTasks: true
11
+
12
+ tasks:
13
+ - id: "investigate"
14
+ title: "Investigate: ${story.title}"
15
+ description: "Reproduce the bug, identify the root cause, and document findings."
16
+ estimationPercent: 30
17
+ activity: "Requirements"
18
+ assignTo: "@ParentAssignee"
19
+ estimationPercentCondition:
20
+ - condition:
21
+ field: "Priority"
22
+ operator: "lte"
23
+ value: 2
24
+ percent: 40
25
+
26
+ - id: "fix"
27
+ title: "Fix: ${story.title}"
28
+ description: "Implement the fix for the identified root cause."
29
+ estimationPercent: 40
30
+ activity: "Development"
31
+ assignTo: "@ParentAssignee"
32
+ dependsOn: ["investigate"]
33
+
34
+ - id: "verify"
35
+ title: "Verify Fix: ${story.title}"
36
+ description: "Verify the fix resolves the issue and does not introduce regressions."
37
+ estimationPercent: 20
38
+ activity: "Testing"
39
+ dependsOn: ["fix"]
40
+
41
+ - id: "close"
42
+ title: "Close & Document: ${story.title}"
43
+ description: "Update documentation, close the bug, and notify stakeholders."
44
+ estimationPercent: 10
45
+ activity: "Documentation"
46
+ dependsOn: ["verify"]
47
+
48
+ estimation:
49
+ strategy: "percentage"
50
+ rounding: "none"
51
+ ifParentHasNoEstimation: "warn"
52
+
53
+ validation:
54
+ totalEstimationMustBe: 100
@@ -0,0 +1,214 @@
1
+ version: "1.0"
2
+ name: "Custom Example — Saved Query"
3
+ description: "Same task breakdown as the custom example, but work item selection is delegated to an existing Azure DevOps saved query instead of a structured filter. Useful when your team already maintains a query that encodes complex area path, iteration, and tag logic."
4
+ author: "Atomize"
5
+ tags: []
6
+
7
+ filter:
8
+ savedQuery:
9
+ path: "Shared Queries/Teams/Platform/Ready for Sprint"
10
+ excludeIfHasTasks: true
11
+
12
+ tasks:
13
+ - title: "Analysis & Playback"
14
+ estimationPercent: 20
15
+ activity: "Development"
16
+ tags: []
17
+ assignTo: "@ParentAssignee"
18
+ condition:
19
+ all:
20
+ - field: tags
21
+ operator: not-contains
22
+ value: Testing Only
23
+ - field: tags
24
+ operator: not-contains
25
+ value: Test Only
26
+
27
+ - title: "Build"
28
+ estimationPercent: 50
29
+ activity: "Development"
30
+ tags: []
31
+ assignTo: "@ParentAssignee"
32
+ condition:
33
+ all:
34
+ - field: tags
35
+ operator: not-contains
36
+ value: Testing Only
37
+ - field: tags
38
+ operator: not-contains
39
+ value: Test Only
40
+
41
+ - title: "Developer Test"
42
+ estimationPercent: 25
43
+ activity: "Development"
44
+ tags: []
45
+ assignTo: "@ParentAssignee"
46
+ condition:
47
+ all:
48
+ - field: tags
49
+ operator: not-contains
50
+ value: Testing Only
51
+ - field: tags
52
+ operator: not-contains
53
+ value: Test Only
54
+
55
+ - title: "QA Smoke Test"
56
+ estimationPercent: 0
57
+ activity: "Development"
58
+ tags: []
59
+ assignTo: "@ParentAssignee"
60
+ condition:
61
+ all:
62
+ - field: tags
63
+ operator: not-contains
64
+ value: Testing Only
65
+ - field: tags
66
+ operator: not-contains
67
+ value: Test Only
68
+
69
+ - title: "Wiki"
70
+ estimationPercent: 5
71
+ activity: "Development"
72
+ tags: []
73
+ assignTo: "@ParentAssignee"
74
+ condition:
75
+ all:
76
+ - field: tags
77
+ operator: not-contains
78
+ value: Testing Only
79
+ - field: tags
80
+ operator: not-contains
81
+ value: Test Only
82
+
83
+ - title: "Code Review"
84
+ estimationPercent: 0
85
+ activity: "Development"
86
+ tags: []
87
+ assignTo: "@Unassigned"
88
+ condition:
89
+ all:
90
+ - field: tags
91
+ operator: not-contains
92
+ value: Testing Only
93
+ - field: tags
94
+ operator: not-contains
95
+ value: Test Only
96
+
97
+ - title: "Physical Data Model Updated"
98
+ estimationPercent: 0
99
+ activity: "Development"
100
+ tags: []
101
+ assignTo: "@ParentAssignee"
102
+ condition:
103
+ all:
104
+ - field: tags
105
+ operator: not-contains
106
+ value: Testing Only
107
+ - field: tags
108
+ operator: not-contains
109
+ value: Test Only
110
+
111
+ - title: "Test Preparation"
112
+ estimationPercent: 40
113
+ estimationPercentCondition:
114
+ - condition:
115
+ any:
116
+ - field: tags
117
+ operator: contains
118
+ value: Testing Only
119
+ - field: tags
120
+ operator: contains
121
+ value: Test Only
122
+ percent: 59.70
123
+ activity: "Testing"
124
+ tags: []
125
+ condition:
126
+ field: tags
127
+ operator: not-contains
128
+ value: Dev Only
129
+ assignTo: "@Unassigned"
130
+
131
+ - title: "Test Execution"
132
+ estimationPercent: 27
133
+ estimationPercentCondition:
134
+ - condition:
135
+ any:
136
+ - field: tags
137
+ operator: contains
138
+ value: Testing Only
139
+ - field: tags
140
+ operator: contains
141
+ value: Test Only
142
+ percent: 40.30
143
+ activity: "Testing"
144
+ tags: []
145
+ condition:
146
+ field: tags
147
+ operator: not-contains
148
+ value: Dev Only
149
+ assignTo: "@Unassigned"
150
+
151
+ - title: "Automation Testing"
152
+ estimationPercent: 0
153
+ activity: "Test Automation"
154
+ tags: []
155
+ condition:
156
+ field: tags
157
+ operator: not-contains
158
+ value: Dev Only
159
+ assignTo: "@Unassigned"
160
+
161
+ - title: "Test Review"
162
+ estimationPercent: 0
163
+ activity: "Testing"
164
+ tags: []
165
+ condition:
166
+ field: tags
167
+ operator: not-contains
168
+ value: Dev Only
169
+ assignTo: "@Unassigned"
170
+
171
+ - title: "Release Notes"
172
+ estimationPercent: 0
173
+ activity: "Testing"
174
+ tags: []
175
+ condition:
176
+ field: tags
177
+ operator: not-contains
178
+ value: Dev Only
179
+ assignTo: "@Unassigned"
180
+
181
+ - title: "Merge to Stable"
182
+ estimationPercent: 10
183
+ activity: "Development"
184
+ tags: []
185
+ assignTo: "@ParentAssignee"
186
+ condition:
187
+ all:
188
+ - field: tags
189
+ operator: not-contains
190
+ value: Testing Only
191
+ - field: tags
192
+ operator: not-contains
193
+ value: Test Only
194
+
195
+ estimation:
196
+ strategy: "percentage"
197
+ rounding: "none"
198
+ minimumTaskPoints: 0
199
+
200
+ metadata:
201
+ category: "Agile Project"
202
+ recommendedFor: []
203
+ estimationGuidelines: |
204
+ Identical task breakdown to the structured-filter custom example.
205
+ The only difference is in the filter: work item selection is handled
206
+ entirely by the saved query at the configured path. The query should
207
+ already encode area path, iteration, and tag constraints so this
208
+ template stays clean. Update the savedQuery path to match your project.
209
+
210
+ Handles three story archetypes in one template:
211
+ - Dev and Test story (no special tag): dev tasks + test tasks, base percentages apply.
212
+ - Testing Only / Test Only story: dev tasks skipped; Test Preparation gets 59.70%
213
+ and Test Execution gets 40.30%.
214
+ - Dev Only story: test tasks skipped.