engineering-process 0.1.0__py3-none-any.whl

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 (69) hide show
  1. engineering_process/__init__.py +3 -0
  2. engineering_process/_download_worker.py +73 -0
  3. engineering_process/_supervisor_posix.py +162 -0
  4. engineering_process/_supervisor_windows.py +148 -0
  5. engineering_process/_windows_job.py +419 -0
  6. engineering_process/bootstrap.py +217 -0
  7. engineering_process/bundles.py +81 -0
  8. engineering_process/cli.py +1012 -0
  9. engineering_process/contracts.py +1274 -0
  10. engineering_process/distribution.py +90 -0
  11. engineering_process/environment.py +753 -0
  12. engineering_process/helper_launch.py +37 -0
  13. engineering_process/lifecycle.py +1024 -0
  14. engineering_process/managed.py +89 -0
  15. engineering_process/markdown.py +78 -0
  16. engineering_process/publication.py +463 -0
  17. engineering_process/requirements-build.txt +1 -0
  18. engineering_process/requirements-dev.txt +10 -0
  19. engineering_process/requirements-runtime.txt +3 -0
  20. engineering_process/runner.py +191 -0
  21. engineering_process/runtime.py +55 -0
  22. engineering_process/skills.py +118 -0
  23. engineering_process/supervision.py +72 -0
  24. engineering_process/syncing.py +398 -0
  25. engineering_process/tooling.py +891 -0
  26. engineering_process-0.1.0.data/data/share/engineering-process/bundles.json +47 -0
  27. engineering_process-0.1.0.data/data/share/engineering-process/examples/change.json +26 -0
  28. engineering_process-0.1.0.data/data/share/engineering-process/examples/plan.json +28 -0
  29. engineering_process-0.1.0.data/data/share/engineering-process/examples/project.json +54 -0
  30. engineering_process-0.1.0.data/data/share/engineering-process/examples/review.json +31 -0
  31. engineering_process-0.1.0.data/data/share/engineering-process/schemas/change.schema.json +88 -0
  32. engineering_process-0.1.0.data/data/share/engineering-process/schemas/lifecycle.schema.json +122 -0
  33. engineering_process-0.1.0.data/data/share/engineering-process/schemas/plan.schema.json +105 -0
  34. engineering_process-0.1.0.data/data/share/engineering-process/schemas/process-lock.schema.json +39 -0
  35. engineering_process-0.1.0.data/data/share/engineering-process/schemas/project.schema.json +515 -0
  36. engineering_process-0.1.0.data/data/share/engineering-process/schemas/review.schema.json +140 -0
  37. engineering_process-0.1.0.data/data/share/engineering-process/schemas/reviewer-attestation.schema.json +30 -0
  38. engineering_process-0.1.0.data/data/share/engineering-process/schemas/verification.schema.json +120 -0
  39. engineering_process-0.1.0.data/data/share/engineering-process/skills/assess-design/SKILL.md +39 -0
  40. engineering_process-0.1.0.data/data/share/engineering-process/skills/build-frontend/SKILL.md +38 -0
  41. engineering_process-0.1.0.data/data/share/engineering-process/skills/build-frontend-foundation/SKILL.md +36 -0
  42. engineering_process-0.1.0.data/data/share/engineering-process/skills/change-api/SKILL.md +36 -0
  43. engineering_process-0.1.0.data/data/share/engineering-process/skills/cross-repo-change/SKILL.md +37 -0
  44. engineering_process-0.1.0.data/data/share/engineering-process/skills/define-change-contract/SKILL.md +40 -0
  45. engineering_process-0.1.0.data/data/share/engineering-process/skills/design-module/SKILL.md +37 -0
  46. engineering_process-0.1.0.data/data/share/engineering-process/skills/evolve-process/SKILL.md +37 -0
  47. engineering_process-0.1.0.data/data/share/engineering-process/skills/finish-change/SKILL.md +34 -0
  48. engineering_process-0.1.0.data/data/share/engineering-process/skills/govern-ui/SKILL.md +40 -0
  49. engineering_process-0.1.0.data/data/share/engineering-process/skills/implement-change/SKILL.md +41 -0
  50. engineering_process-0.1.0.data/data/share/engineering-process/skills/implement-module/SKILL.md +36 -0
  51. engineering_process-0.1.0.data/data/share/engineering-process/skills/implement-use-case/SKILL.md +36 -0
  52. engineering_process-0.1.0.data/data/share/engineering-process/skills/integrate-mcp/SKILL.md +37 -0
  53. engineering_process-0.1.0.data/data/share/engineering-process/skills/maintain-docs/SKILL.md +35 -0
  54. engineering_process-0.1.0.data/data/share/engineering-process/skills/plan-change/SKILL.md +37 -0
  55. engineering_process-0.1.0.data/data/share/engineering-process/skills/publish-change/SKILL.md +38 -0
  56. engineering_process-0.1.0.data/data/share/engineering-process/skills/review-change/SKILL.md +49 -0
  57. engineering_process-0.1.0.data/data/share/engineering-process/skills/run-change/SKILL.md +51 -0
  58. engineering_process-0.1.0.data/data/share/engineering-process/skills/run-change/references/execution.md +88 -0
  59. engineering_process-0.1.0.data/data/share/engineering-process/skills/run-project-command/SKILL.md +55 -0
  60. engineering_process-0.1.0.data/data/share/engineering-process/skills/specify-use-case/SKILL.md +37 -0
  61. engineering_process-0.1.0.data/data/share/engineering-process/skills/verify-change/SKILL.md +38 -0
  62. engineering_process-0.1.0.data/data/share/engineering-process/templates/AGENTS.process.md +17 -0
  63. engineering_process-0.1.0.data/data/share/engineering-process/templates/PULL_REQUEST_TEMPLATE.md +27 -0
  64. engineering_process-0.1.0.dist-info/METADATA +387 -0
  65. engineering_process-0.1.0.dist-info/RECORD +69 -0
  66. engineering_process-0.1.0.dist-info/WHEEL +5 -0
  67. engineering_process-0.1.0.dist-info/entry_points.txt +2 -0
  68. engineering_process-0.1.0.dist-info/licenses/LICENSE +21 -0
  69. engineering_process-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,47 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "bundles": {
4
+ "core": [
5
+ "define-change-contract",
6
+ "evolve-process",
7
+ "finish-change",
8
+ "implement-change",
9
+ "plan-change",
10
+ "review-change",
11
+ "run-change",
12
+ "verify-change"
13
+ ],
14
+ "delivery": [
15
+ "assess-design",
16
+ "run-project-command"
17
+ ],
18
+ "product": [
19
+ "implement-use-case",
20
+ "specify-use-case"
21
+ ],
22
+ "architecture": [
23
+ "design-module",
24
+ "implement-module"
25
+ ],
26
+ "api": [
27
+ "change-api"
28
+ ],
29
+ "frontend": [
30
+ "build-frontend",
31
+ "build-frontend-foundation",
32
+ "govern-ui"
33
+ ],
34
+ "mcp": [
35
+ "integrate-mcp"
36
+ ],
37
+ "docs": [
38
+ "maintain-docs"
39
+ ],
40
+ "publication": [
41
+ "publish-change"
42
+ ],
43
+ "cross-repo": [
44
+ "cross-repo-change"
45
+ ]
46
+ }
47
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "id": "issue-123",
4
+ "summary": "Add a bounded product capability",
5
+ "source": "issue-123",
6
+ "comparisonBase": "main",
7
+ "specification": {
8
+ "kind": "project",
9
+ "reference": "docs/specs/issue-123.md",
10
+ "rationale": "The project specification owns observable product behavior."
11
+ },
12
+ "risk": "medium",
13
+ "affectedProjects": ["sample-project"],
14
+ "acceptanceCriteria": [
15
+ {
16
+ "id": "ac-1",
17
+ "outcome": "The authorized user can complete the requested behavior"
18
+ }
19
+ ],
20
+ "requiredProfiles": ["development", "review"],
21
+ "signOff": {
22
+ "required": false,
23
+ "status": "not-required",
24
+ "evidence": null
25
+ }
26
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "changeId": "issue-123",
4
+ "contractDigest": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
5
+ "approach": "Implement the accepted behavior through its existing owner and preserve the current public contract.",
6
+ "workItems": [
7
+ {
8
+ "id": "work-1",
9
+ "outcome": "Implement and prove the accepted behavior",
10
+ "affectedPaths": ["src/", "tests/"],
11
+ "verificationProfiles": ["development", "review"]
12
+ }
13
+ ],
14
+ "acceptancePlan": [
15
+ {
16
+ "criterionId": "ac-1",
17
+ "workItems": ["work-1"],
18
+ "verificationProfiles": ["development", "review"]
19
+ }
20
+ ],
21
+ "risks": [
22
+ {
23
+ "risk": "Authorization could be bypassed by a new path.",
24
+ "mitigation": "Reuse the policy owner and prove unauthorized access is rejected."
25
+ }
26
+ ],
27
+ "openDecisions": []
28
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "schemaVersion": 3,
3
+ "project": "sample-project",
4
+ "lifecycle": {
5
+ "requiredProfiles": ["development", "review"]
6
+ },
7
+ "profiles": {
8
+ "development": [
9
+ {
10
+ "id": "unit",
11
+ "run": ["python", "-m", "unittest"],
12
+ "timeoutSeconds": 600
13
+ }
14
+ ],
15
+ "review": [
16
+ {
17
+ "id": "full",
18
+ "run": ["python", "-m", "unittest", "discover"],
19
+ "timeoutSeconds": 1800
20
+ }
21
+ ]
22
+ },
23
+ "environment": {
24
+ "defaultProfile": "development",
25
+ "foregroundOnly": true,
26
+ "managedTools": [],
27
+ "profiles": {
28
+ "development": [
29
+ "python-runtime"
30
+ ],
31
+ "review": [
32
+ "python-runtime"
33
+ ]
34
+ },
35
+ "requirements": [
36
+ {
37
+ "id": "python-runtime",
38
+ "description": "Supported CPython runtime",
39
+ "probe": {
40
+ "run": [
41
+ "python",
42
+ "--version"
43
+ ],
44
+ "timeoutSeconds": 15,
45
+ "readOnly": true,
46
+ "outputStream": "combined",
47
+ "outputRegex": "^Python 3\\.(?:11|12|13|14)\\."
48
+ },
49
+ "remediation": "Install a supported Python runtime before running processctl."
50
+ }
51
+ ],
52
+ "setupActions": []
53
+ }
54
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "changeId": "issue-123",
4
+ "cycle": 1,
5
+ "checkpoint": "0123456789abcdef",
6
+ "workspaceFingerprint": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
7
+ "comparisonBase": "main",
8
+ "reviewer": {
9
+ "actorId": "reviewer-1",
10
+ "contextId": "review-session-1",
11
+ "kind": "agent"
12
+ },
13
+ "independence": {
14
+ "method": "isolated-context",
15
+ "attestedBy": "agent-host",
16
+ "evidence": "The host created a read-only context that did not participate in implementation."
17
+ },
18
+ "verdict": "changes-requested",
19
+ "findings": [
20
+ {
21
+ "id": "finding-1",
22
+ "severity": "high",
23
+ "path": "src/authorization.py",
24
+ "line": 42,
25
+ "summary": "Authorization is missing",
26
+ "evidence": "The unsafe operation has no policy check",
27
+ "status": "open",
28
+ "resolutionEvidence": null
29
+ }
30
+ ]
31
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://engineering-process.invalid/schemas/change.schema.json",
4
+ "title": "Engineering change specification",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "id",
10
+ "summary",
11
+ "source",
12
+ "comparisonBase",
13
+ "specification",
14
+ "risk",
15
+ "affectedProjects",
16
+ "acceptanceCriteria",
17
+ "requiredProfiles",
18
+ "signOff"
19
+ ],
20
+ "properties": {
21
+ "$schema": {"type": "string"},
22
+ "schemaVersion": {"const": 2},
23
+ "id": {
24
+ "type": "string",
25
+ "maxLength": 64,
26
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
27
+ },
28
+ "summary": {"type": "string", "minLength": 1, "maxLength": 500},
29
+ "source": {"type": "string", "minLength": 1, "maxLength": 1000},
30
+ "comparisonBase": {"type": "string", "minLength": 1, "maxLength": 256},
31
+ "specification": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "required": ["kind", "reference", "rationale"],
35
+ "properties": {
36
+ "kind": {"enum": ["project", "change-contract"]},
37
+ "reference": {"type": "string", "minLength": 1, "maxLength": 1000},
38
+ "rationale": {"type": "string", "minLength": 1, "maxLength": 2000}
39
+ }
40
+ },
41
+ "risk": {"enum": ["low", "medium", "high"]},
42
+ "affectedProjects": {
43
+ "type": "array",
44
+ "minItems": 1,
45
+ "uniqueItems": true,
46
+ "items": {
47
+ "type": "string",
48
+ "pattern": "^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$"
49
+ }
50
+ },
51
+ "acceptanceCriteria": {
52
+ "type": "array",
53
+ "minItems": 1,
54
+ "items": {
55
+ "type": "object",
56
+ "additionalProperties": false,
57
+ "required": ["id", "outcome"],
58
+ "properties": {
59
+ "id": {
60
+ "type": "string",
61
+ "maxLength": 64,
62
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
63
+ },
64
+ "outcome": {"type": "string", "minLength": 1, "maxLength": 1000}
65
+ }
66
+ }
67
+ },
68
+ "requiredProfiles": {
69
+ "type": "array",
70
+ "minItems": 1,
71
+ "uniqueItems": true,
72
+ "items": {
73
+ "type": "string",
74
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
75
+ }
76
+ },
77
+ "signOff": {
78
+ "type": "object",
79
+ "additionalProperties": false,
80
+ "required": ["required", "status", "evidence"],
81
+ "properties": {
82
+ "required": {"type": "boolean"},
83
+ "status": {"enum": ["not-required", "pending", "approved"]},
84
+ "evidence": {"type": ["string", "null"], "maxLength": 1000}
85
+ }
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,122 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://engineering-process.invalid/schemas/lifecycle.schema.json",
4
+ "title": "Engineering change lifecycle state",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "changeId",
10
+ "project",
11
+ "phase",
12
+ "cycle",
13
+ "revision",
14
+ "comparisonBase",
15
+ "contract",
16
+ "plan",
17
+ "implementationActors",
18
+ "verification",
19
+ "pendingFindings",
20
+ "reviewAssignment",
21
+ "review",
22
+ "completion",
23
+ "history"
24
+ ],
25
+ "properties": {
26
+ "schemaVersion": {"const": 2},
27
+ "changeId": {"type": "string"},
28
+ "project": {"type": "string"},
29
+ "phase": {
30
+ "enum": [
31
+ "specified",
32
+ "planned",
33
+ "implementing",
34
+ "verified",
35
+ "review-pending",
36
+ "changes-requested",
37
+ "approved",
38
+ "completed"
39
+ ]
40
+ },
41
+ "cycle": {"type": "integer", "minimum": 1},
42
+ "revision": {"type": "integer", "minimum": 1},
43
+ "comparisonBase": {"type": "string"},
44
+ "contract": {"$ref": "#/$defs/artifact"},
45
+ "plan": {"anyOf": [{"$ref": "#/$defs/artifact"}, {"type": "null"}]},
46
+ "implementationActors": {"type": "array", "items": {"$ref": "#/$defs/actor"}},
47
+ "verification": {"type": "array", "items": {"$ref": "#/$defs/evidence"}},
48
+ "pendingFindings": {"type": "array", "items": {"$ref": "#/$defs/finding"}},
49
+ "reviewAssignment": {"type": ["object", "null"]},
50
+ "review": {"anyOf": [{"$ref": "#/$defs/artifact"}, {"type": "null"}]},
51
+ "completion": {"anyOf": [{"$ref": "#/$defs/artifact"}, {"type": "null"}]},
52
+ "history": {"type": "array", "minItems": 1, "items": {"type": "object"}}
53
+ },
54
+ "$defs": {
55
+ "actor": {
56
+ "type": "object",
57
+ "required": ["actorId", "contextId", "kind"],
58
+ "properties": {
59
+ "actorId": {"type": "string"},
60
+ "contextId": {"type": "string"},
61
+ "kind": {"enum": ["agent", "human"]}
62
+ }
63
+ },
64
+ "artifact": {
65
+ "type": "object",
66
+ "required": ["path", "digest"],
67
+ "properties": {
68
+ "path": {"type": "string"},
69
+ "digest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"}
70
+ }
71
+ },
72
+ "evidence": {
73
+ "type": "object",
74
+ "required": ["profile", "path", "digest", "checkpoint", "workspaceFingerprint"],
75
+ "properties": {
76
+ "profile": {"type": "string"},
77
+ "path": {"type": "string"},
78
+ "digest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
79
+ "checkpoint": {"type": "string"},
80
+ "workspaceFingerprint": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"}
81
+ }
82
+ },
83
+ "finding": {
84
+ "type": "object",
85
+ "additionalProperties": false,
86
+ "required": [
87
+ "id",
88
+ "severity",
89
+ "path",
90
+ "line",
91
+ "summary",
92
+ "evidence",
93
+ "status",
94
+ "resolutionEvidence"
95
+ ],
96
+ "properties": {
97
+ "id": {"type": "string"},
98
+ "severity": {"enum": ["critical", "high", "medium", "low"]},
99
+ "path": {"type": "string"},
100
+ "line": {"type": ["integer", "null"], "minimum": 1},
101
+ "summary": {"type": "string"},
102
+ "evidence": {"type": "string"},
103
+ "status": {"enum": ["open", "deferred"]},
104
+ "resolutionEvidence": {"type": ["string", "null"], "maxLength": 4000}
105
+ },
106
+ "allOf": [
107
+ {
108
+ "if": {"properties": {"status": {"const": "open"}}},
109
+ "then": {"properties": {"resolutionEvidence": {"type": "null"}}}
110
+ },
111
+ {
112
+ "if": {"properties": {"status": {"const": "deferred"}}},
113
+ "then": {
114
+ "properties": {
115
+ "resolutionEvidence": {"type": "string", "minLength": 1}
116
+ }
117
+ }
118
+ }
119
+ ]
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,105 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://engineering-process.invalid/schemas/plan.schema.json",
4
+ "title": "Engineering implementation plan",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "changeId",
10
+ "contractDigest",
11
+ "approach",
12
+ "workItems",
13
+ "acceptancePlan",
14
+ "risks",
15
+ "openDecisions"
16
+ ],
17
+ "properties": {
18
+ "$schema": {"type": "string"},
19
+ "schemaVersion": {"const": 1},
20
+ "changeId": {
21
+ "type": "string",
22
+ "maxLength": 64,
23
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
24
+ },
25
+ "contractDigest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"},
26
+ "approach": {"type": "string", "minLength": 1, "maxLength": 4000},
27
+ "workItems": {
28
+ "type": "array",
29
+ "minItems": 1,
30
+ "items": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["id", "outcome", "affectedPaths", "verificationProfiles"],
34
+ "properties": {
35
+ "id": {
36
+ "type": "string",
37
+ "maxLength": 64,
38
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
39
+ },
40
+ "outcome": {"type": "string", "minLength": 1, "maxLength": 1000},
41
+ "affectedPaths": {
42
+ "type": "array",
43
+ "minItems": 1,
44
+ "uniqueItems": true,
45
+ "items": {"type": "string", "minLength": 1, "maxLength": 1000}
46
+ },
47
+ "verificationProfiles": {
48
+ "type": "array",
49
+ "minItems": 1,
50
+ "uniqueItems": true,
51
+ "items": {
52
+ "type": "string",
53
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ },
59
+ "acceptancePlan": {
60
+ "type": "array",
61
+ "minItems": 1,
62
+ "items": {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "required": ["criterionId", "workItems", "verificationProfiles"],
66
+ "properties": {
67
+ "criterionId": {
68
+ "type": "string",
69
+ "maxLength": 64,
70
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"
71
+ },
72
+ "workItems": {
73
+ "type": "array",
74
+ "minItems": 1,
75
+ "uniqueItems": true,
76
+ "items": {"type": "string"}
77
+ },
78
+ "verificationProfiles": {
79
+ "type": "array",
80
+ "minItems": 1,
81
+ "uniqueItems": true,
82
+ "items": {"type": "string"}
83
+ }
84
+ }
85
+ }
86
+ },
87
+ "risks": {
88
+ "type": "array",
89
+ "items": {
90
+ "type": "object",
91
+ "additionalProperties": false,
92
+ "required": ["risk", "mitigation"],
93
+ "properties": {
94
+ "risk": {"type": "string", "minLength": 1, "maxLength": 1000},
95
+ "mitigation": {"type": "string", "minLength": 1, "maxLength": 1000}
96
+ }
97
+ }
98
+ },
99
+ "openDecisions": {
100
+ "type": "array",
101
+ "uniqueItems": true,
102
+ "items": {"type": "string", "minLength": 1, "maxLength": 1000}
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://engineering-process.invalid/schemas/process-lock.schema.json",
4
+ "title": "Engineering process lock",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "process", "skills"],
8
+ "properties": {
9
+ "$schema": {
10
+ "type": "string"
11
+ },
12
+ "schemaVersion": {
13
+ "const": 1
14
+ },
15
+ "process": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "required": ["version", "digest"],
19
+ "properties": {
20
+ "version": {
21
+ "type": "string"
22
+ },
23
+ "digest": {
24
+ "type": "string",
25
+ "pattern": "^sha256:[0-9a-f]{64}$"
26
+ }
27
+ }
28
+ },
29
+ "skills": {
30
+ "type": "array",
31
+ "minItems": 1,
32
+ "uniqueItems": true,
33
+ "items": {
34
+ "type": "string",
35
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
36
+ }
37
+ }
38
+ }
39
+ }