specdrive-cli 0.1.3 → 0.1.8
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.
- package/README.md +25 -14
- package/commands/gates.js +26 -16
- package/commands/router.js +2 -41
- package/commands/tools.json +15 -20
- package/package.json +1 -1
- package/schemas/plan.schema.json +94 -16
- package/schemas/spec.schema.json +140 -17
- package/schemas/tasks.schema.json +57 -14
- package/schemas/traceability.schema.json +41 -12
- package/schemas/workflow-state.schema.json +40 -13
- package/scripts/anti-redundancy.js +11 -11
- package/scripts/audit-log.js +14 -2
- package/scripts/generate-adapters.js +28 -28
- package/scripts/generate-from-template.js +1 -0
- package/scripts/load-plugins.js +15 -6
- package/scripts/team.js +79 -6
- package/scripts/test-adapters.js +20 -29
- package/scripts/test-end-to-end.js +1 -4
- package/scripts/test-router.js +10 -4
- package/scripts/test-state-transitions.js +0 -1
- package/scripts/test-validator.js +7 -2
- package/scripts/validate-governance.js +29 -64
- package/src/index.js +71 -8
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
```markdown
|
|
1
3
|
# SpecDrive
|
|
2
4
|
|
|
3
5
|
**Enterprise Spec-Driven Development for AI Coding Tools**
|
|
@@ -31,6 +33,7 @@ It adds:
|
|
|
31
33
|
- [Validation](#validation)
|
|
32
34
|
- [CI/CD Integration](#cicd-integration)
|
|
33
35
|
- [Tool Support](#tool-support)
|
|
36
|
+
- [Team Collaboration](#team-collaboration)
|
|
34
37
|
- [Contributing](#contributing)
|
|
35
38
|
- [License](#license)
|
|
36
39
|
|
|
@@ -83,9 +86,12 @@ Inside your project root, run:
|
|
|
83
86
|
sdrive init
|
|
84
87
|
```
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
SpecDrive will ask you to select which AI tools you use.
|
|
90
|
+
|
|
91
|
+
It then creates the `.sdrive/` folder and tool adapters for the selected tools.
|
|
87
92
|
|
|
88
93
|
---
|
|
94
|
+
|
|
89
95
|
## Where to Run Commands
|
|
90
96
|
|
|
91
97
|
| Command Type | Where to Run | Example |
|
|
@@ -96,6 +102,7 @@ This creates the `.sdrive/` folder and tool adapters.
|
|
|
96
102
|
**Rule:** If it starts with `sdrive`, run it in the terminal. If it starts with `/sdrive:`, run it in your AI chat.
|
|
97
103
|
|
|
98
104
|
---
|
|
105
|
+
|
|
99
106
|
## Quickstart
|
|
100
107
|
|
|
101
108
|
1. Initialize the project:
|
|
@@ -434,6 +441,8 @@ jobs:
|
|
|
434
441
|
|
|
435
442
|
## Tool Support
|
|
436
443
|
|
|
444
|
+
SpecDrive supports 15 AI tools.
|
|
445
|
+
|
|
437
446
|
| Tool | Adapter |
|
|
438
447
|
|------|---------|
|
|
439
448
|
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
@@ -441,8 +450,20 @@ jobs:
|
|
|
441
450
|
| Claude Code | `.claude/commands/sdrive.md` |
|
|
442
451
|
| Cursor | `.cursor/rules/sdrive.md` |
|
|
443
452
|
| VS Code | `.vscode/sdrive-commands.json` |
|
|
453
|
+
| Amazon Q Developer | `.amazonq/rules/sdrive.md` |
|
|
454
|
+
| Gemini CLI | `.gemini/commands/sdrive.md` |
|
|
455
|
+
| Continue | `.continue/rules/sdrive.md` |
|
|
456
|
+
| Codex | `.codex/rules/sdrive.md` |
|
|
457
|
+
| Windsurf | `.windsurf/rules/sdrive.md` |
|
|
458
|
+
| Kilo Code | `.kilocode/rules/sdrive.md` |
|
|
459
|
+
| OpenCode | `.opencode/rules/sdrive.md` |
|
|
460
|
+
| Qoder | `.qoder/rules/sdrive.md` |
|
|
461
|
+
| Qwen Code | `.qwen/rules/sdrive.md` |
|
|
462
|
+
| Rovo Dev CLI | `.rovo/rules/sdrive.md` |
|
|
444
463
|
|
|
445
|
-
|
|
464
|
+
Adapters are generated automatically when you run `sdrive init`.
|
|
465
|
+
|
|
466
|
+
During initialization, you select which tools you use. Only selected tools get adapters.
|
|
446
467
|
|
|
447
468
|
---
|
|
448
469
|
|
|
@@ -465,6 +486,7 @@ sdrive team:update bob reviewer
|
|
|
465
486
|
sdrive team:remove carol
|
|
466
487
|
```
|
|
467
488
|
|
|
489
|
+
---
|
|
468
490
|
|
|
469
491
|
## Contributing
|
|
470
492
|
|
|
@@ -481,15 +503,4 @@ MIT
|
|
|
481
503
|
|
|
482
504
|
---
|
|
483
505
|
|
|
484
|
-
This is now
|
|
485
|
-
|
|
486
|
-
- Table of contents
|
|
487
|
-
- Installation steps
|
|
488
|
-
- Core workflow in correct order
|
|
489
|
-
- Detailed optional commands
|
|
490
|
-
- Agent reference
|
|
491
|
-
- Directory structure
|
|
492
|
-
- Validation and CI/CD sections
|
|
493
|
-
- Tool support
|
|
494
|
-
|
|
495
|
-
You can now save it as `README.md`.
|
|
506
|
+
This README is now clean and correct.
|
package/commands/gates.js
CHANGED
|
@@ -7,7 +7,7 @@ function getWorkflowState() {
|
|
|
7
7
|
const workflowStatePath = path.join(projectRoot, ".sdrive", "workflow-state.json");
|
|
8
8
|
|
|
9
9
|
if (!fs.existsSync(workflowStatePath)) {
|
|
10
|
-
console.error("❌ workflow-state.json not found.");
|
|
10
|
+
console.error("❌ workflow-state.json not found. Run `sdrive init` first.");
|
|
11
11
|
process.exit(1);
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -19,14 +19,28 @@ function saveWorkflowState(state) {
|
|
|
19
19
|
fs.writeFileSync(workflowStatePath, JSON.stringify(state, null, 2));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function
|
|
22
|
+
function getFeature(featureName) {
|
|
23
23
|
const state = getWorkflowState();
|
|
24
|
+
return state.features?.find((f) => f.name === featureName);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function gateStatus(featureName, gate) {
|
|
28
|
+
const feature = getFeature(featureName);
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
if (!feature) {
|
|
31
|
+
return "unknown";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return feature.gates?.[gate] || "pending";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function approveGate(featureName, gate) {
|
|
38
|
+
const state = getWorkflowState();
|
|
39
|
+
const feature = getFeature(featureName);
|
|
26
40
|
|
|
27
41
|
if (!feature) {
|
|
28
42
|
console.error(`❌ Feature "${featureName}" not found.`);
|
|
29
|
-
|
|
43
|
+
return false;
|
|
30
44
|
}
|
|
31
45
|
|
|
32
46
|
if (!feature.gates) {
|
|
@@ -43,22 +57,18 @@ function approveGate(featureName, gate) {
|
|
|
43
57
|
|
|
44
58
|
saveWorkflowState(state);
|
|
45
59
|
console.log(`✅ Gate "${gate}" approved for feature "${featureName}".`);
|
|
60
|
+
return true;
|
|
46
61
|
}
|
|
47
62
|
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
const feature = state.features?.find((f) => f.name === featureName);
|
|
51
|
-
|
|
52
|
-
if (!feature) {
|
|
53
|
-
return "unknown";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return feature.gates?.[gate] || "pending";
|
|
63
|
+
function isGateApproved(featureName, gate) {
|
|
64
|
+
return gateStatus(featureName, gate) === "approved";
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
module.exports = {
|
|
60
|
-
approveGate,
|
|
61
|
-
gateStatus,
|
|
62
68
|
getWorkflowState,
|
|
63
|
-
saveWorkflowState
|
|
69
|
+
saveWorkflowState,
|
|
70
|
+
getFeature,
|
|
71
|
+
gateStatus,
|
|
72
|
+
approveGate,
|
|
73
|
+
isGateApproved
|
|
64
74
|
};
|
package/commands/router.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const { execSync } = require("child_process");
|
|
6
|
-
const { getWorkflowState, saveWorkflowState } = require("./gates");
|
|
6
|
+
const { getWorkflowState, saveWorkflowState, isGateApproved } = require("./gates");
|
|
7
7
|
|
|
8
8
|
const projectRoot = process.cwd();
|
|
9
9
|
|
|
@@ -55,44 +55,6 @@ function runValidators(validators) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function checkGate(featureName, gate) {
|
|
59
|
-
const state = getWorkflowState();
|
|
60
|
-
const feature = state.features?.find((f) => f.name === featureName);
|
|
61
|
-
|
|
62
|
-
if (!feature) {
|
|
63
|
-
console.error(`❌ Feature "${featureName}" not found.`);
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return feature.gates?.[gate] === "approved";
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function approveGate(featureName, gate) {
|
|
71
|
-
const state = getWorkflowState();
|
|
72
|
-
const feature = state.features?.find((f) => f.name === featureName);
|
|
73
|
-
|
|
74
|
-
if (!feature) {
|
|
75
|
-
console.error(`❌ Feature "${featureName}" not found.`);
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!feature.gates) {
|
|
80
|
-
feature.gates = {};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
feature.gates[gate] = "approved";
|
|
84
|
-
|
|
85
|
-
feature.history = feature.history || [];
|
|
86
|
-
feature.history.push({
|
|
87
|
-
event: `${gate}_approved`,
|
|
88
|
-
timestamp: new Date().toISOString()
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
saveWorkflowState(state);
|
|
92
|
-
console.log(`✅ Gate "${gate}" approved for feature "${featureName}".`);
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
58
|
function toKebabCase(text) {
|
|
97
59
|
return text
|
|
98
60
|
.toLowerCase()
|
|
@@ -129,7 +91,6 @@ function main() {
|
|
|
129
91
|
console.log(`📁 Agent File: ${command.agentFile}`);
|
|
130
92
|
console.log(`📝 Description: ${command.description}`);
|
|
131
93
|
|
|
132
|
-
// Gate enforcement
|
|
133
94
|
if (command.gates && command.gates.length > 0) {
|
|
134
95
|
if (!featureName) {
|
|
135
96
|
console.error(`❌ This command requires a feature name or description.`);
|
|
@@ -140,7 +101,7 @@ function main() {
|
|
|
140
101
|
console.log(`\n🚧 Required Gates: ${command.gates.join(", ")}`);
|
|
141
102
|
|
|
142
103
|
for (const gate of command.gates) {
|
|
143
|
-
if (!
|
|
104
|
+
if (!isGateApproved(featureName, gate)) {
|
|
144
105
|
console.log(`\n⏸️ Gate "${gate}" is NOT approved for "${featureName}".`);
|
|
145
106
|
console.log(` Please approve this gate before running this command.`);
|
|
146
107
|
console.log(` Use: sdrive approve ${gate}\n`);
|
package/commands/tools.json
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"tools": [
|
|
4
|
-
{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"name": "vscode-native",
|
|
22
|
-
"adapterPath": ".vscode/sdrive-commands.json"
|
|
23
|
-
}
|
|
4
|
+
{ "name": "github-copilot", "adapterPath": ".github/copilot-instructions.md" },
|
|
5
|
+
{ "name": "cline", "adapterPath": ".clinerules/sdrive.md" },
|
|
6
|
+
{ "name": "claude-code", "adapterPath": ".claude/commands/sdrive.md" },
|
|
7
|
+
{ "name": "cursor", "adapterPath": ".cursor/rules/sdrive.md" },
|
|
8
|
+
{ "name": "vscode-native", "adapterPath": ".vscode/sdrive-commands.json" },
|
|
9
|
+
{ "name": "amazon-q", "adapterPath": ".amazonq/rules/sdrive.md" },
|
|
10
|
+
{ "name": "gemini-cli", "adapterPath": ".gemini/commands/sdrive.md" },
|
|
11
|
+
{ "name": "continue", "adapterPath": ".continue/rules/sdrive.md" },
|
|
12
|
+
{ "name": "codex", "adapterPath": ".codex/rules/sdrive.md" },
|
|
13
|
+
{ "name": "windsurf", "adapterPath": ".windsurf/rules/sdrive.md" },
|
|
14
|
+
{ "name": "kilo-code", "adapterPath": ".kilocode/rules/sdrive.md" },
|
|
15
|
+
{ "name": "opencode", "adapterPath": ".opencode/rules/sdrive.md" },
|
|
16
|
+
{ "name": "qoder", "adapterPath": ".qoder/rules/sdrive.md" },
|
|
17
|
+
{ "name": "qwen-code", "adapterPath": ".qwen/rules/sdrive.md" },
|
|
18
|
+
{ "name": "rovo-dev", "adapterPath": ".rovo/rules/sdrive.md" }
|
|
24
19
|
]
|
|
25
20
|
}
|
package/package.json
CHANGED
package/schemas/plan.schema.json
CHANGED
|
@@ -1,28 +1,47 @@
|
|
|
1
1
|
{
|
|
2
|
-
"": "http://json-schema.org/draft-07/schema#",
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"title": "Technical Plan",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
6
|
-
"feature": {
|
|
7
|
-
|
|
6
|
+
"feature": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"pattern": "^[a-z0-9-]+$"
|
|
9
|
+
},
|
|
10
|
+
"version": {
|
|
11
|
+
"type": "integer",
|
|
12
|
+
"minimum": 1
|
|
13
|
+
},
|
|
8
14
|
"architecture": {
|
|
9
15
|
"type": "object",
|
|
10
16
|
"properties": {
|
|
11
|
-
"approach": {
|
|
17
|
+
"approach": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"minLength": 10
|
|
20
|
+
},
|
|
12
21
|
"components": {
|
|
13
22
|
"type": "array",
|
|
14
23
|
"items": {
|
|
15
24
|
"type": "object",
|
|
16
25
|
"properties": {
|
|
17
26
|
"name": { "type": "string" },
|
|
18
|
-
"action": {
|
|
27
|
+
"action": {
|
|
28
|
+
"enum": ["create", "modify", "reuse", "delete"]
|
|
29
|
+
},
|
|
19
30
|
"description": { "type": "string" }
|
|
20
31
|
},
|
|
21
32
|
"required": ["name", "action"]
|
|
22
33
|
}
|
|
34
|
+
},
|
|
35
|
+
"referencedRequirementIds": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"pattern": "^(FR|NFR)-[0-9]{3}$"
|
|
40
|
+
},
|
|
41
|
+
"uniqueItems": true
|
|
23
42
|
}
|
|
24
43
|
},
|
|
25
|
-
"required": ["approach", "components"]
|
|
44
|
+
"required": ["approach", "components", "referencedRequirementIds"]
|
|
26
45
|
},
|
|
27
46
|
"dataModel": {
|
|
28
47
|
"type": "object",
|
|
@@ -33,7 +52,9 @@
|
|
|
33
52
|
"type": "object",
|
|
34
53
|
"properties": {
|
|
35
54
|
"name": { "type": "string" },
|
|
36
|
-
"action": {
|
|
55
|
+
"action": {
|
|
56
|
+
"enum": ["create", "update", "no_change", "delete"]
|
|
57
|
+
},
|
|
37
58
|
"fields": {
|
|
38
59
|
"type": "array",
|
|
39
60
|
"items": {
|
|
@@ -42,7 +63,9 @@
|
|
|
42
63
|
"name": { "type": "string" },
|
|
43
64
|
"type": { "type": "string" },
|
|
44
65
|
"nullable": { "type": "boolean" },
|
|
45
|
-
"action": {
|
|
66
|
+
"action": {
|
|
67
|
+
"enum": ["add", "modify", "remove", "no_change"]
|
|
68
|
+
}
|
|
46
69
|
},
|
|
47
70
|
"required": ["name", "type", "action"]
|
|
48
71
|
}
|
|
@@ -51,7 +74,10 @@
|
|
|
51
74
|
"required": ["name", "action", "fields"]
|
|
52
75
|
}
|
|
53
76
|
},
|
|
54
|
-
"flowDescription": {
|
|
77
|
+
"flowDescription": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"minLength": 10
|
|
80
|
+
}
|
|
55
81
|
},
|
|
56
82
|
"required": ["entities", "flowDescription"]
|
|
57
83
|
},
|
|
@@ -60,30 +86,82 @@
|
|
|
60
86
|
"items": {
|
|
61
87
|
"type": "object",
|
|
62
88
|
"properties": {
|
|
63
|
-
"method": {
|
|
89
|
+
"method": {
|
|
90
|
+
"enum": ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
|
91
|
+
},
|
|
64
92
|
"path": { "type": "string" },
|
|
65
|
-
"action": {
|
|
93
|
+
"action": {
|
|
94
|
+
"enum": ["create", "modify", "reuse", "delete"]
|
|
95
|
+
},
|
|
66
96
|
"authenticationRequired": { "type": "boolean" },
|
|
67
97
|
"linkedRequirements": {
|
|
68
98
|
"type": "array",
|
|
69
|
-
"items": {
|
|
99
|
+
"items": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"pattern": "^(FR|NFR)-[0-9]{3}$"
|
|
102
|
+
}
|
|
70
103
|
}
|
|
71
104
|
},
|
|
72
105
|
"required": ["method", "path", "action", "linkedRequirements"]
|
|
73
106
|
}
|
|
74
107
|
},
|
|
108
|
+
"securityConsiderations": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"properties": {
|
|
113
|
+
"requirementId": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"pattern": "^NFR-[0-9]{3}$"
|
|
116
|
+
},
|
|
117
|
+
"description": { "type": "string" }
|
|
118
|
+
},
|
|
119
|
+
"required": ["requirementId", "description"]
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"performanceConsiderations": {
|
|
123
|
+
"type": "array",
|
|
124
|
+
"items": {
|
|
125
|
+
"type": "object",
|
|
126
|
+
"properties": {
|
|
127
|
+
"requirementId": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"pattern": "^NFR-[0-9]{3}$"
|
|
130
|
+
},
|
|
131
|
+
"strategy": { "type": "string" }
|
|
132
|
+
},
|
|
133
|
+
"required": ["requirementId", "strategy"]
|
|
134
|
+
}
|
|
135
|
+
},
|
|
75
136
|
"dependencies": {
|
|
76
137
|
"type": "array",
|
|
77
138
|
"items": {
|
|
78
139
|
"type": "object",
|
|
79
140
|
"properties": {
|
|
80
141
|
"name": { "type": "string" },
|
|
81
|
-
"type": {
|
|
82
|
-
|
|
142
|
+
"type": {
|
|
143
|
+
"enum": ["library", "service", "internal", "external"]
|
|
144
|
+
},
|
|
145
|
+
"action": {
|
|
146
|
+
"enum": ["add", "update", "remove", "no_change"]
|
|
147
|
+
},
|
|
148
|
+
"notes": { "type": "string" }
|
|
83
149
|
},
|
|
84
150
|
"required": ["name", "type", "action"]
|
|
85
151
|
}
|
|
152
|
+
},
|
|
153
|
+
"risks": {
|
|
154
|
+
"type": "array",
|
|
155
|
+
"items": {
|
|
156
|
+
"type": "object",
|
|
157
|
+
"properties": {
|
|
158
|
+
"description": { "type": "string" },
|
|
159
|
+
"mitigation": { "type": "string" }
|
|
160
|
+
},
|
|
161
|
+
"required": ["description", "mitigation"]
|
|
162
|
+
}
|
|
86
163
|
}
|
|
87
164
|
},
|
|
88
|
-
"required": ["feature", "version", "architecture", "dataModel"]
|
|
89
|
-
|
|
165
|
+
"required": ["feature", "version", "architecture", "dataModel"],
|
|
166
|
+
"additionalProperties": false
|
|
167
|
+
}
|
package/schemas/spec.schema.json
CHANGED
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
{
|
|
2
|
-
"": "http://json-schema.org/draft-07/schema#",
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"title": "Feature Specification",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
6
|
-
"feature": {
|
|
7
|
-
|
|
6
|
+
"feature": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"pattern": "^[a-z0-9-]+$",
|
|
9
|
+
"description": "Machine-friendly feature name (kebab-case)"
|
|
10
|
+
},
|
|
11
|
+
"title": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"minLength": 3,
|
|
14
|
+
"description": "Human-readable feature title"
|
|
15
|
+
},
|
|
16
|
+
"version": {
|
|
17
|
+
"type": "integer",
|
|
18
|
+
"minimum": 1
|
|
19
|
+
},
|
|
8
20
|
"overview": {
|
|
9
21
|
"type": "object",
|
|
10
22
|
"properties": {
|
|
11
|
-
"problemStatement": {
|
|
12
|
-
|
|
13
|
-
|
|
23
|
+
"problemStatement": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 10
|
|
26
|
+
},
|
|
27
|
+
"proposedSolution": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"minLength": 10
|
|
30
|
+
},
|
|
31
|
+
"targetAudience": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"minLength": 3
|
|
34
|
+
},
|
|
35
|
+
"assumptions": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": { "type": "string" }
|
|
38
|
+
},
|
|
39
|
+
"outOfScope": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": { "type": "string" }
|
|
42
|
+
}
|
|
14
43
|
},
|
|
15
44
|
"required": ["problemStatement", "proposedSolution", "targetAudience"]
|
|
16
45
|
},
|
|
@@ -20,31 +49,60 @@
|
|
|
20
49
|
"items": {
|
|
21
50
|
"type": "object",
|
|
22
51
|
"properties": {
|
|
23
|
-
"id": {
|
|
24
|
-
|
|
52
|
+
"id": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"pattern": "^US-[0-9]{3}$"
|
|
55
|
+
},
|
|
56
|
+
"title": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 5
|
|
59
|
+
},
|
|
60
|
+
"role": { "type": "string" },
|
|
61
|
+
"want": { "type": "string" },
|
|
62
|
+
"benefit": { "type": "string" },
|
|
25
63
|
"acceptanceCriteria": {
|
|
26
64
|
"type": "array",
|
|
27
65
|
"minItems": 1,
|
|
28
66
|
"items": {
|
|
29
67
|
"type": "object",
|
|
30
68
|
"properties": {
|
|
31
|
-
"id": {
|
|
32
|
-
|
|
69
|
+
"id": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"pattern": "^AC-[0-9]{3}$"
|
|
72
|
+
},
|
|
73
|
+
"text": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"minLength": 10
|
|
76
|
+
}
|
|
33
77
|
},
|
|
34
78
|
"required": ["id", "text"]
|
|
35
79
|
}
|
|
36
80
|
}
|
|
37
81
|
},
|
|
38
|
-
"required": ["id", "title", "acceptanceCriteria"]
|
|
82
|
+
"required": ["id", "title", "role", "want", "benefit", "acceptanceCriteria"]
|
|
39
83
|
}
|
|
40
84
|
},
|
|
41
85
|
"functionalRequirements": {
|
|
42
86
|
"type": "array",
|
|
87
|
+
"minItems": 1,
|
|
43
88
|
"items": {
|
|
44
89
|
"type": "object",
|
|
45
90
|
"properties": {
|
|
46
|
-
"id": {
|
|
47
|
-
|
|
91
|
+
"id": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"pattern": "^FR-[0-9]{3}$"
|
|
94
|
+
},
|
|
95
|
+
"text": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"minLength": 10
|
|
98
|
+
},
|
|
99
|
+
"linkedAcceptanceCriteria": {
|
|
100
|
+
"type": "array",
|
|
101
|
+
"items": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"pattern": "^AC-[0-9]{3}$"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
48
106
|
},
|
|
49
107
|
"required": ["id", "text"]
|
|
50
108
|
}
|
|
@@ -54,12 +112,77 @@
|
|
|
54
112
|
"items": {
|
|
55
113
|
"type": "object",
|
|
56
114
|
"properties": {
|
|
57
|
-
"id": {
|
|
58
|
-
|
|
115
|
+
"id": {
|
|
116
|
+
"type": "string",
|
|
117
|
+
"pattern": "^NFR-[0-9]{3}$"
|
|
118
|
+
},
|
|
119
|
+
"text": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"minLength": 10
|
|
122
|
+
},
|
|
123
|
+
"linkedAcceptanceCriteria": {
|
|
124
|
+
"type": "array",
|
|
125
|
+
"items": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"pattern": "^AC-[0-9]{3}$"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
59
130
|
},
|
|
60
131
|
"required": ["id", "text"]
|
|
61
132
|
}
|
|
133
|
+
},
|
|
134
|
+
"edgeCases": {
|
|
135
|
+
"type": "array",
|
|
136
|
+
"items": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"properties": {
|
|
139
|
+
"id": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"pattern": "^EC-[0-9]{3}$"
|
|
142
|
+
},
|
|
143
|
+
"description": { "type": "string" },
|
|
144
|
+
"expectedBehavior": { "type": "string" }
|
|
145
|
+
},
|
|
146
|
+
"required": ["id", "description", "expectedBehavior"]
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"dependencies": {
|
|
150
|
+
"type": "array",
|
|
151
|
+
"items": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"properties": {
|
|
154
|
+
"name": { "type": "string" },
|
|
155
|
+
"type": {
|
|
156
|
+
"enum": ["library", "service", "internal", "external"]
|
|
157
|
+
},
|
|
158
|
+
"notes": { "type": "string" }
|
|
159
|
+
},
|
|
160
|
+
"required": ["name", "type"]
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"risks": {
|
|
164
|
+
"type": "array",
|
|
165
|
+
"items": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"properties": {
|
|
168
|
+
"id": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"pattern": "^RISK-[0-9]{3}$"
|
|
171
|
+
},
|
|
172
|
+
"description": { "type": "string" },
|
|
173
|
+
"mitigation": { "type": "string" }
|
|
174
|
+
},
|
|
175
|
+
"required": ["id", "description", "mitigation"]
|
|
176
|
+
}
|
|
62
177
|
}
|
|
63
178
|
},
|
|
64
|
-
"required": [
|
|
65
|
-
|
|
179
|
+
"required": [
|
|
180
|
+
"feature",
|
|
181
|
+
"title",
|
|
182
|
+
"version",
|
|
183
|
+
"overview",
|
|
184
|
+
"userStories",
|
|
185
|
+
"functionalRequirements"
|
|
186
|
+
],
|
|
187
|
+
"additionalProperties": false
|
|
188
|
+
}
|