specdrive-cli 0.1.2 → 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 +35 -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**
|
|
@@ -21,6 +23,7 @@ It adds:
|
|
|
21
23
|
- [Why SpecDrive](#why-specdrive)
|
|
22
24
|
- [Prerequisites](#prerequisites)
|
|
23
25
|
- [Installation](#installation)
|
|
26
|
+
- [Where to Run Commands](#where-to-run-commands)
|
|
24
27
|
- [Quickstart](#quickstart)
|
|
25
28
|
- [Core Workflow](#core-workflow)
|
|
26
29
|
- [Optional Commands](#optional-commands)
|
|
@@ -30,6 +33,7 @@ It adds:
|
|
|
30
33
|
- [Validation](#validation)
|
|
31
34
|
- [CI/CD Integration](#cicd-integration)
|
|
32
35
|
- [Tool Support](#tool-support)
|
|
36
|
+
- [Team Collaboration](#team-collaboration)
|
|
33
37
|
- [Contributing](#contributing)
|
|
34
38
|
- [License](#license)
|
|
35
39
|
|
|
@@ -82,7 +86,20 @@ Inside your project root, run:
|
|
|
82
86
|
sdrive init
|
|
83
87
|
```
|
|
84
88
|
|
|
85
|
-
|
|
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.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Where to Run Commands
|
|
96
|
+
|
|
97
|
+
| Command Type | Where to Run | Example |
|
|
98
|
+
|--------------|--------------|---------|
|
|
99
|
+
| Terminal command | In your terminal / shell | `sdrive init` |
|
|
100
|
+
| Slash command | In your AI tool's chat | `/sdrive:propose "User login"` |
|
|
101
|
+
|
|
102
|
+
**Rule:** If it starts with `sdrive`, run it in the terminal. If it starts with `/sdrive:`, run it in your AI chat.
|
|
86
103
|
|
|
87
104
|
---
|
|
88
105
|
|
|
@@ -424,6 +441,8 @@ jobs:
|
|
|
424
441
|
|
|
425
442
|
## Tool Support
|
|
426
443
|
|
|
444
|
+
SpecDrive supports 15 AI tools.
|
|
445
|
+
|
|
427
446
|
| Tool | Adapter |
|
|
428
447
|
|------|---------|
|
|
429
448
|
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
@@ -431,8 +450,20 @@ jobs:
|
|
|
431
450
|
| Claude Code | `.claude/commands/sdrive.md` |
|
|
432
451
|
| Cursor | `.cursor/rules/sdrive.md` |
|
|
433
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` |
|
|
434
463
|
|
|
435
|
-
|
|
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.
|
|
436
467
|
|
|
437
468
|
---
|
|
438
469
|
|
|
@@ -455,6 +486,7 @@ sdrive team:update bob reviewer
|
|
|
455
486
|
sdrive team:remove carol
|
|
456
487
|
```
|
|
457
488
|
|
|
489
|
+
---
|
|
458
490
|
|
|
459
491
|
## Contributing
|
|
460
492
|
|
|
@@ -471,15 +503,4 @@ MIT
|
|
|
471
503
|
|
|
472
504
|
---
|
|
473
505
|
|
|
474
|
-
This is now
|
|
475
|
-
|
|
476
|
-
- Table of contents
|
|
477
|
-
- Installation steps
|
|
478
|
-
- Core workflow in correct order
|
|
479
|
-
- Detailed optional commands
|
|
480
|
-
- Agent reference
|
|
481
|
-
- Directory structure
|
|
482
|
-
- Validation and CI/CD sections
|
|
483
|
-
- Tool support
|
|
484
|
-
|
|
485
|
-
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
|
+
}
|