Repository: stoplightio/spectral
Pull request #773 (2019-11-16 15:03:15)

feat: drop oas2 and oas3 rulesets
#689 Has been superseded by #772
This PR superseds #772 

This is an attempt at helping get all the tests pass.

nulltoken: @philsturgeon Making progress. Still one karma failing test. 
nulltoken: @philsturgeon Jest and harness tests have been fixed. Still got an issue with the last one.
It fails differently on node 12 and node 10 (#737 should help fix fix by introducing a stable sort).

Feel free to cherry pick and squash the proposed changes you like (don't hate too much?) in your PR.

I'm going to decorate some of the changes I'm the less happy with with some comments.
nulltoken: @philsturgeon rebased on top of your latest changes
philsturgeon: @nulltoken this is amazing! Shall I leave this in your capable hands? @P0lip can you help him get this over the line? 
P0lip: @philsturgeon yup! will do my best to help.
nulltoken: > @nulltoken this is amazing! 

@philsturgeon YOU did the hard work. All I had to do was fighting against jest matchers (Yes. Looking at you `expectArrayContaining` and `expectObjectContaining)

>Shall I leave this in your capable hands? @P0lip can you help him get this over the line?

Ok. Taking over this task then.
nulltoken: @P0lip It's now rebased. I've started to reduce a bit the diff noise by removing unneeded changes.

Could you please start a first pass of review?

philsturgeon: Minimise the change in order to get this one done. We don’t need to do all the things I got carried away.

On Thu, Nov 21, 2019 at 16:22, Jakub Rożek <notifications@github.com> wrote:

> @P0lip commented on this pull request.
>
> ---------------------------------------------------------------
>
> In [docs/getting-started/rulesets.md](https://github.com/stoplightio/spectral/pull/773#discussion_r349183997):
>
>>      formats: ["oas3"]
> -    given: "$"
> +    given: "$.servers"
>
> To mitigate the risk, I'd suggest reverting these for now.
> We can revisit them in a separate PR.
> What do you think?
> As I described in Phil's PR, field with given vs given with no field they are not equivalent in certain circumstances.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, [view it on GitHub](https://github.com/stoplightio/spectral/pull/773?email_source=notifications&email_token=AAAQONNZCYYUK2LEVKTTWODQU2YVVA5CNFSM4JOFQL4KYY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCMRENJA#pullrequestreview-321013412), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AAAQONMPGDP37LHEQREAHZDQU2YVVANCNFSM4JOFQL4A).
nulltoken: Copied over from https://github.com/stoplightio/spectral/pull/772#discussion_r348962825

>cc @nulltoken since you are working on that too.
>I'm pretty sure src/rulesets/oas3/tests/api-servers.ts fails now.

@P0lip I don't see it failing in #773. Or is it passing for the wrong reason? Or did I misunderstand you?
nulltoken: @P0lip I'll work on this tomorrow. Should by ready for review by Monday.
P0lip: > @P0lip I don't see it failing in #773. Or is it passing for the wrong reason? Or did I misunderstand you?

It's not failing, because the rule looks the same.
```json
    "oas3-api-servers": {
      "description": "OpenAPI `servers` must be present and non-empty array.",
      "recommended": true,
      "formats": ["oas3"],
      "given": "$",
      "then": {
        "field": "servers",
        "function": "schema",
        "functionOptions": {
          "schema": {
            "items": {
              "type": "object"
            },
            "minItems": 1,
            "type": "array"
          }
        }
      },
      "tags": [
        "api"
      ]
    },
```
nulltoken: >Minimise the change in order to get this one done. 

@philsturgeon @P0lip I've tried to reduce the diff noise as much as possible.
I'll try to get rid of some of the added `registerFormat`.
nulltoken: > 
> 
> > Minimise the change in order to get this one done.
> 
> @philsturgeon @P0lip I've tried to reduce the diff noise as much as possible.
> I'll try to get rid of some of the added `registerFormat`.

@P0lip @philsturgeon Looks like the tests fail if I remove those new `registerFormat()` calls. However, I'm not quite sure why they're now required whereas they previously weren't.


philsturgeon: I added formats to a lot of rules whereas before they were applied at the ruleset level. The way we pluck rules and put them into setRules in our tests means this is now important. Our test stuff is a little messy but we should clean it up in another thing, let’s just get it working first.

On Sat, Nov 23, 2019 at 11:33, nulltoken <notifications@github.com> wrote:

>>> Minimise the change in order to get this one done.
>>
>> [@philsturgeon](https://github.com/philsturgeon) [@P0lip](https://github.com/P0lip) I've tried to reduce the diff noise as much as possible.
>> I'll try to get rid of some of the added registerFormat.
>
> [@P0lip](https://github.com/P0lip) [@philsturgeon](https://github.com/philsturgeon) Looks like the tests fail if I remove those new registerFormat() calls. However, I'm not quite sure why they're now required whereas they previously weren't.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, [view it on GitHub](https://github.com/stoplightio/spectral/pull/773?email_source=notifications&email_token=AAAQONIJHQN2ZTLYCRIIKZLQVEIH5A5CNFSM4JOFQL4KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEE7TIOI#issuecomment-557790265), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AAAQONJSSZTHZFEMXIN2R7DQVEIH5ANCNFSM4JOFQL4A).
nulltoken: > I added formats to a lot of rules whereas before they were applied at the ruleset level. The way we pluck rules and put them into setRules in our tests means this is now important. Our test stuff is a little messy but we should clean it up in another thing, let’s just get it working first.

@philsturgeon Thanks for this feedback. How I now realize that, as a user, I may not have clearly understood that subtlety. 

- The doc states `Spectral supports two core formats: oas2 and oas3. Using registerFormat you can add support for autodetecting other formats.` This __seems__ to say "Hey! You're linting OAS2 or OAS3? Cool! Everything is already set up.".  Or is this a cli vs Api difference in usage?

- I my mind, when a ruleset was exposing formats, rules that didn't specify a format were inheriting those ruleset formats. I'm not sure to understand why specifying now a format at the rule level requires to previously invoke `registerFormat`. Or was that inheritance thingie all in my head only?

Considering this, when one should invoke `registerFormat` seems a bit "blurry" to me. Any chance you could sprinkle a bit of your magic documentation writing dust to enlighten this?
nulltoken: Rebased and cleaned up history.
P0lip: @nulltoken I'll review it today!
nulltoken: > Looking very well!
> Almost ready to be merged.

@p0lip I believe all comments have been dealt with.

Patch:
diff --git a/docs/getting-started/rulesets.md b/docs/getting-started/rulesets.md
index 8418fb2d..140cd71e 100644
--- a/docs/getting-started/rulesets.md
+++ b/docs/getting-started/rulesets.md
@@ -78,9 +78,8 @@ Specifying the format is optional, so you can completely ignore this if all the
 
 ```yaml
 rules:
-  api-servers:
+  oas3-api-servers:
     description: "OpenAPI `servers` must be present and non-empty array."
-    recommended: true
     formats: ["oas3"]
     given: "$"
     then:
@@ -101,7 +100,7 @@ Alternatively, formats can be specified at the ruleset level:
 ```yaml
 formats: ["oas3"]
 rules:
-  api-servers:
+  oas3-api-servers:
     description: "OpenAPI `servers` must be present and non-empty array."
     recommended: true
     given: "$"
@@ -163,12 +162,10 @@ responses:
 
 ## Extending rules
 
-Rulesets can extend other rulesets. For example, Spectral comes with two built in rulesets - one for OpenAPI v2 (`spectral:oas2`), and one for OpenAPI v3 (`spectral:oas3`). 
-
-Use the `extends` property in your ruleset file to build upon or customize other rulesets.
+Rulesets can extend other rulesets using the `extends` property. This can be used to build upon or customize other rulesets.
 
 ```yaml
-extends: spectral:oas2
+extends: spectral:oas
 rules:
   my-rule-name:
     description: Tags must have a description.
@@ -193,41 +190,38 @@ extends:
 Sometimes you might want to apply specific rules from another ruleset. Use the `extends` property, and pass `off` as the second argument in order to add the rules from another ruleset, but disable them all by default. This allows you to pick and choose which rules you would like to enable.
 
 ```yaml
-extends: [[spectral:oas2, off]]
+extends: [[spectral:oas, off]]
 rules:
-  # This rule is defined in the spectral:oas2 ruleset. We're passing `true` to turn it on and inherit the severity defined in the spectral:oas2 ruleset.
+  # This rule is defined in the spectral:oas ruleset. We're passing `true` to turn it on and inherit the severity defined in the spectral:oas ruleset.
   operation-operationId-unique: true
 ```
 
-The example above will run the single rule that we enabled, since we passed `off` to disable all rules by default when extending the `spectral:oas2` ruleset.
+The example above will run the single rule that we enabled, since we passed `off` to disable all rules by default when extending the `spectral:oas` ruleset.
 
 ## Disabling rules
 
 This example shows the opposite of the "Enabling Specific rules" example. Sometimes you might want to enable all rules by default, and disable a few.
 
 ```yaml
-extends: [[spectral:oas2, all]]
+extends: [[spectral:oas, all]]
 rules:
   operation-operationId-unique: false
 ```
 
-The example above will run all of the rules defined in the `spectral:oas2` ruleset (rather than the default behavior that runs only the recommended ones), with one exceptions - we turned `operation-operationId-unique` off.
+The example above will run all of the rules defined in the `spectral:oas` ruleset (rather than the default behavior that runs only the recommended ones), with one exceptions - we turned `operation-operationId-unique` off.
 
-The current recommended rules are marked with the property `recommended: true` in their respective rulesets:
+- [Rules relevant to OpenAPI v2 and v3](../reference/openapi-rules.md)
 
-- [Rules relevant to both OpenAPI v2 and v3](https://github.com/stoplightio/spectral/tree/master/src/rulesets/oas/index.json)
-- [Rules specific to only OpenAPI v2](https://github.com/stoplightio/spectral/tree/master/src/rulesets/oas2/index.json)
-- [Rules specific to only OpenAPI v3](https://github.com/stoplightio/spectral/tree/master/src/rulesets/oas3/index.json)
 
 ## Changing rule severity
 
 ```yaml
-extends: spectral:oas2
+extends: spectral:oas
 rules:
   operation-2xx-response: warn
 ```
 
-The example above will run the recommended rules from the `spectral:oas2` ruleset, but report `operation-2xx-response` as a warning rather than as an error (as is the default behavior in the `spectral:oas2` ruleset).
+The example above will run the recommended rules from the `spectral:oas` ruleset, but report `operation-2xx-response` as a warning rather than as an error (as is the default behavior in the `spectral:oas` ruleset).
 
 Available severity levels are `error`, `warn`, `info`, `hint`, and `off`.
 
@@ -252,7 +246,6 @@ message: "{{value}} is greater than 0"
 
 message: "{{path}} cannot point at remote reference"
 ```
-
 ## Creating custom functions
 
 Learn more about [custom functions](../guides/custom-functions.md).
diff --git a/docs/guides/javascript.md b/docs/guides/javascript.md
index 670807cd..a2b3445e 100644
--- a/docs/guides/javascript.md
+++ b/docs/guides/javascript.md
@@ -107,21 +107,22 @@ openapi: 3.0.0
 `
 
 const spectral = new Spectral();
-spectral.registerFormat('oas2', isOpenApiv2);
-spectral.registerFormat('oas3', isOpenApiv3);spectral.loadRuleset('spectral:oas3') // spectral:oas2 for OAS 2.0 aka Swagger
+spectral.registerFormat('oas2', isOpenApiv2); // spectral:oas2 for OpenAPI v2.0
+spectral.registerFormat('oas3', isOpenApiv3);
+spectral.loadRuleset('spectral:oas')
   .then(() => spectral.run(myOpenApiDocument))
   .then(results => {
     console.log('here are the results', results);
   });
 ``` 
 
-The OpenAPI rules are opinionated. There might be some rules that you prefer to change. We encourage you to create your rules to fit your use case. We welcome additions to the existing rulesets as well!
+The OpenAPI rules are opinionated. There might be some rules that you prefer to change, or disable. We encourage you to create your rules to fit your use case, and we welcome additions to the existing rulesets as well!
 
 ## Advanced
 
 ### Creating a custom format
 
-Spectral supports two core formats: `oas2` and `oas3`. Using `registerFormat` you can add support for autodetecting other formats. You might want to do this for a ruleset which is run against multiple major versions of description format like RAML v0.8 and v1.0.
+Spectral supports two core formats: `oas2` and `oas3`. Using `registerFormat` you can add support for auto-detecting other formats. You might want to do this for a ruleset which is run against multiple major versions of description format like RAML v0.8 and v1.0.
 
 ```js
 const { Spectral } = require('@stoplight/spectral');
diff --git a/docs/reference/functions.md b/docs/reference/functions.md
index 700fd24d..10b89cc2 100644
--- a/docs/reference/functions.md
+++ b/docs/reference/functions.md
@@ -150,7 +150,7 @@ schema | a valid JSON Schema document | yes
 <!-- title: example -->
 
 ```yaml
-api-servers:
+oas3-api-servers:
   description: "OpenAPI `servers` must be present and non-empty array."
   recommended: true
   type: "style"
diff --git a/docs/reference/openapi-rules.md b/docs/reference/openapi-rules.md
index 6fa1411d..7a86f073 100644
--- a/docs/reference/openapi-rules.md
+++ b/docs/reference/openapi-rules.md
@@ -1,18 +1,12 @@
 # OpenAPI Rules
 
-Spectral has three rulesets built in: 
+Spectral has a built-in "oas" ruleset, with OAS being shorthand for the [OpenAPI Specification](https://openapis.org/specification).
 
-- [oas](#oas)
-- [oas2](#oas2)
-- [oas3](#oas3)
+In your ruleset file you can add `extends: "spectral:oas"` and you'll get all of the following rules applied, depending on the appropriate OpenAPI version used (detected through [formats](../getting-started/rulesets.md#formats)).
 
-_OAS is shorthand for OpenAPI Specification._
+## OpenAPI v2 & v3
 
-In your ruleset file you can add `extends: "spectral:oas"` and you'll get everything from `spectral:oas2` and `spectral:oas3`, meaning you can lint either type of document and only the relevant rules will apply thanks to [formats](../getting-started/rulesets.md#formats).
-
-Let's look at the rules in these rulesets.
-
-## oas
+These rules apply to both OpenAPI v2 and v3.
 
 ### operation-2xx-response
 
@@ -120,7 +114,7 @@ info:
 
 Examples for `requestBody` or response examples can have an `externalValue` or a `value`, but they cannot have both.
 
-**Recommended:** No
+**Recommended:** Yes
 
 **Bad Example**
 
@@ -222,7 +216,7 @@ info:
 
 This rule protects against an edge case, for anyone bringing in description documents from third parties and using the parsed content rendered in HTML/JS. If one of those third parties does something shady like inject `eval()` JavaScript statements, it could lead to an XSS attack. 
 
-**Recommended:** No
+**Recommended:** Yes
 
 **Bad Example**
 
@@ -283,7 +277,7 @@ Why? Well, you _can_ reference tags arbitrarily in operations, and definition is
 
 Defining tags allows you to add more information like a `description`. For more information see [tag-description](#tag-description).
 
-**Recommended:** No
+**Recommended:** Yes
 
 ### operation-default-response
 
@@ -324,6 +318,11 @@ Use just one tag for an operation, which is helpful for some documentation syste
 
 ### operation-summary-formatted
 
+<!-- theme: warning -->
+> ### Removed in v5.0
+>
+> This rule was removed in Spectral v5.0, so if you are relying on it you can find the [old definition here](https://github.com/stoplightio/spectral/blob/v4.2.0/src/rulesets/oas/index.json#L312) and paste it into your [custom ruleset](../getting-started/rulesets.md).
+
 Operation `summary` should start with upper case and end with a dot.
 
 **Recommended:** No
@@ -381,53 +380,47 @@ tags:
 
 **Recommended:** No
 
-## oas2
+## OpenAPI v2.0-only
 
-These OpenAPI v2.0-only rules can be loaded with `extends: "spectral:oas2"` and include all the oas rules.
+These rules will only apply to OpenAPI v2.0 documents.
 
-### operation-formData-consume-check
+### oas2-operation-formData-consume-check
 
 Operations with an `in: formData` parameter must include `application/x-www-form-urlencoded` or `multipart/form-data` in their `consumes` property.
 
 **Recommended:** Yes
 
-### api-host
+### oas2-api-host
 
 OpenAPI `host` must be present and non-empty string.
 
 **Recommended:** Yes
 
-### api-schemes
+### oas2-api-schemes
 
 OpenAPI host `schemes` must be present and non-empty array.
 
 **Recommended:** Yes
 
-### host-not-example
+### oas2-host-not-example
 
-Server URL should not point at `example.com`.
+Server URL should not point at example.com.
 
 **Recommended:** No
 
-### host-trailing-slash
+### oas2-host-trailing-slash
 
 Server URL should not have a trailing slash.
 
 **Recommended:** Yes
 
-### definition-description
-
-Definition `description` must be present and non-empty string.
-
-**Recommended:** No
-
-### operation-security-defined
+### oas2-operation-security-defined
 
 Operation `security` values must match a scheme defined in the `securityDefinitions` object.
 
 **Recommended:** Yes
 
-### unused-definition
+### oas2-unused-definition
 
 Potential unused reusable `definition` entry has been detected.
 
@@ -437,7 +430,7 @@ specifications that reference those objects).
 
 **Recommended:** Yes
 
-### valid-example
+### oas2-valid-example
 
 Examples must be valid against their defined schema.
 
@@ -467,11 +460,11 @@ Parameter objects should have a `description`.
 
 **Recommended:** No
 
-## oas3
+## OpenAPI v3-only
 
-These OpenAPI v3.0-only rules can be loaded with `extends: "spectral: oas3"` and include all the oas rules.
+These rules will only be applied to OpenAPI v3.0 documents.
 
-### api-servers
+### oas3-api-servers
 
 OpenAPI `servers` must be present and non-empty array.
 
@@ -491,22 +484,15 @@ servers:
 
 If this is going out to the world, maybe have production and a general sandbox people can play with.
 
-
-### components-schema-description
-
-Model `description` must be present and non-empty string.
-
-**Recommended:** No
-
-### operation-security-defined
+### oas3-operation-security-defined
 
 Operation `security` values must match a scheme defined in the `components.securitySchemes` object.
 
 **Recommended:** Yes
 
-### server-not-example.com
+### oas3-server-not-example.com
 
-Server URL should not point at `example.com`.
+Server URL should not point at example.com.
 
 **Recommended:** No
 
@@ -524,7 +510,7 @@ servers:
 
 We have example.com for documentation purposes here, but you should put in actual domains.
 
-### server-trailing-slash
+### oas3-server-trailing-slash
 
 Server URL should not have a trailing slash.
 
@@ -548,7 +534,7 @@ servers:
   - url: https://example.com/api/
 ```
 
-### unused-components-schema
+### oas3-unused-components-schema
 
 Potential unused reusable `schema` entry has been detected.
 
@@ -558,7 +544,7 @@ specifications that reference those objects).
 
 **Recommended:** Yes
 
-### valid-example
+### oas3-valid-example
 
 Examples must be valid against their defined schema.
 
diff --git a/karma-jest.ts b/karma-jest.ts
index e48211ef..3fe2bbd9 100644
--- a/karma-jest.ts
+++ b/karma-jest.ts
@@ -20,7 +20,7 @@ expect.extend({
 
 // @ts-ignore
 test.each = input => (name: string, fn: Function) => {
-  // very simple stub-like implementation needed by src/rulesets/oas3/__tests__/valid-example.ts and src/rulesets/__tests__/validation.test.ts
+  // very simple stub-like implementation needed by src/rulesets/oas/__tests__/valid-example.ts and src/rulesets/__tests__/validation.test.ts
   for (const value of input) {
     if (Array.isArray(value)) {
       fn(...value);
diff --git a/rollup.config.js b/rollup.config.js
index 3d16c18c..722385f2 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -6,20 +6,18 @@ import commonjs from 'rollup-plugin-commonjs';
 import { terser } from 'rollup-plugin-terser';
 
 const BASE_PATH = process.cwd();
-const functions = [];
+const directory = 'dist/rulesets/oas/functions';
+const targetDir = path.join(BASE_PATH, directory);
 
-for (const directory of ['dist/rulesets/oas/functions','dist/rulesets/oas2/functions', 'dist/rulesets/oas3/functions']) {
-  const targetDir = path.join(BASE_PATH, directory);
-  if (!fs.existsSync(targetDir)) continue;
-  for (const file of fs.readdirSync(targetDir)) {
-    const targetFile = path.join(targetDir, file);
-    const stat = fs.statSync(targetFile);
-    if (!stat.isFile()) continue;
-    const ext = path.extname(targetFile);
-    if (ext !== '.js') continue;
+const functions = [];
+for (const file of fs.readdirSync(targetDir)) {
+  const targetFile = path.join(targetDir, file);
+  const stat = fs.statSync(targetFile);
+  if (!stat.isFile()) continue;
+  const ext = path.extname(targetFile);
+  if (ext !== '.js') continue;
 
-    functions.push(targetFile);
-  }
+  functions.push(targetFile);
 }
 
 module.exports = functions.map(fn => ({
diff --git a/scripts/generate-assets.js b/scripts/generate-assets.js
index 914c2cb3..c910ef4a 100755
--- a/scripts/generate-assets.js
+++ b/scripts/generate-assets.js
@@ -30,7 +30,7 @@ const target = path.join(baseDir, `assets.json`);
 const assets = {};
 
 (async () => {
-  await Promise.all(['', '2', '3'].map(spec => processDirectory(assets, path.join(__dirname, `../rulesets/oas${spec}`))));
+  await processDirectory(assets, path.join(__dirname, '../rulesets/oas'));
   await writeFileAsync(target, JSON.stringify(assets, null, 2));
 })();
 
diff --git a/setupKarma.ts b/setupKarma.ts
index bf45de00..93132408 100644
--- a/setupKarma.ts
+++ b/setupKarma.ts
@@ -2,17 +2,9 @@ import * as jsonSpecv4 from 'ajv/lib/refs/json-schema-draft-04.json';
 import { FetchMockSandbox } from 'fetch-mock';
 
 const oasRuleset = JSON.parse(JSON.stringify(require('./rulesets/oas/index.json')));
-const oas2Ruleset = JSON.parse(JSON.stringify(require('./rulesets/oas2/index.json')));
-const oas2Schema = JSON.parse(JSON.stringify(require('./rulesets/oas2/schemas/main.json')));
-const oas3Ruleset = JSON.parse(JSON.stringify(require('./rulesets/oas3/index.json')));
-const oas3Schema = JSON.parse(JSON.stringify(require('./rulesets/oas3/schemas/main.json')));
-
-const oasFunctions = {
-  // import path used for require must be deterministic at build-time (not run-time) in case of Karma, hence no loop can be used
-  '': JSON.parse(JSON.stringify(require('./__karma__/__fixtures__/oas-functions.json'))),
-  '2': JSON.parse(JSON.stringify(require('./__karma__/__fixtures__/oas2-functions.json'))),
-  '3': JSON.parse(JSON.stringify(require('./__karma__/__fixtures__/oas3-functions.json'))),
-};
+const oasFunctions = JSON.parse(JSON.stringify(require('./__karma__/__fixtures__/oas-functions.json')));
+const oas2Schema = JSON.parse(JSON.stringify(require('./rulesets/oas/schemas/schema.oas2.json')));
+const oas3Schema = JSON.parse(JSON.stringify(require('./rulesets/oas/schemas/schema.oas3.json')));
 
 const { fetch } = window;
 let fetchMock: FetchMockSandbox;
@@ -26,33 +18,21 @@ beforeEach(() => {
     body: JSON.parse(JSON.stringify(oasRuleset)),
   });
 
-  fetchMock.get('https://unpkg.com/@stoplight/spectral/rulesets/oas2/index.json', {
-    status: 200,
-    body: JSON.parse(JSON.stringify(oas2Ruleset)),
-  });
-
-  fetchMock.get('https://unpkg.com/@stoplight/spectral/rulesets/oas3/index.json', {
-    status: 200,
-    body: JSON.parse(JSON.stringify(oas3Ruleset)),
-  });
-
-  fetchMock.get('https://unpkg.com/@stoplight/spectral/rulesets/oas2/schemas/main.json', {
+  fetchMock.get('https://unpkg.com/@stoplight/spectral/rulesets/oas/schemas/schema.oas2.json', {
     status: 200,
     body: JSON.parse(JSON.stringify(oas2Schema)),
   });
 
-  fetchMock.get('https://unpkg.com/@stoplight/spectral/rulesets/oas3/schemas/main.json', {
+  fetchMock.get('https://unpkg.com/@stoplight/spectral/rulesets/oas/schemas/schema.oas3.json', {
     status: 200,
     body: JSON.parse(JSON.stringify(oas3Schema)),
   });
 
-  for (const [spec, fns] of Object.entries(oasFunctions)) {
-    for (const [name, fn] of Object.entries<string>(fns)) {
-      fetchMock.get(`https://unpkg.com/@stoplight/spectral/rulesets/oas${spec}/functions/${name}`, {
-        status: 200,
-        body: fn,
-      });
-    }
+  for (const [name, fn] of Object.entries<string>(oasFunctions)) {
+    fetchMock.get(`https://unpkg.com/@stoplight/spectral/rulesets/oas/functions/${name}`, {
+      status: 200,
+      body: fn,
+    });
   }
 
   fetchMock.get('http://json-schema.org/draft-04/schema', {
diff --git a/src/assets.ts b/src/assets.ts
index cf7aadd9..7e9d8543 100644
--- a/src/assets.ts
+++ b/src/assets.ts
@@ -6,8 +6,6 @@ function resolveSpectralRuleset(ruleset: string) {
 
 export const RESOLVE_ALIASES: Dictionary<string, string> = {
   'spectral:oas': resolveSpectralRuleset('oas'),
-  'spectral:oas2': resolveSpectralRuleset('oas2'),
-  'spectral:oas3': resolveSpectralRuleset('oas3'),
 };
 
 export const STATIC_ASSETS: Dictionary<string> = {};
diff --git a/src/rulesets/oas/index.json b/src/rulesets/oas/index.json
index 6773c82a..557861ca 100644
--- a/src/rulesets/oas/index.json
+++ b/src/rulesets/oas/index.json
@@ -1,5 +1,4 @@
 {
-  "extends": ["spectral:oas2", "spectral:oas3"],
   "formats": ["oas2", "oas3"],
   "functions": [
     "oasOp2xxResponse",
@@ -25,9 +24,10 @@
         "operation"
       ]
     },
-    "operation-formData-consume-check": {
+    "oas2-operation-formData-consume-check": {
       "description": "Operations with an `in: formData` parameter must include `application/x-www-form-urlencoded` or `multipart/form-data` in their `consumes` property.",
       "recommended": true,
+      "formats": ["oas2"],
       "type": "validation",
       "given": "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]",
       "then": {
@@ -113,7 +113,7 @@
     },
     "example-value-or-externalValue": {
       "description": "Example should have either a `value` or `externalValue` field.",
-      "recommended": false,
+      "recommended": true,
       "type": "style",
       "given": "$..example",
       "then": {
@@ -180,7 +180,7 @@
     },
     "no-eval-in-markdown": {
       "description": "Markdown descriptions should not contain `eval(`.",
-      "recommended": false,
+      "recommended": true,
       "type": "style",
       "given": "$..*",
       "then": [
@@ -322,22 +322,6 @@
         "operation"
       ]
     },
-    "operation-summary-formatted": {
-      "description": "Operation `summary` should start with upper case and end with a dot.",
-      "recommended": false,
-      "type": "style",
-      "given": "$.paths.*[?( @property === 'get' || @property === 'put' || @property === 'post' || @property === 'delete' || @property === 'options' || @property === 'head' || @property === 'patch' || @property === 'trace' )]",
-      "then": {
-        "field": "summary",
-        "function": "pattern",
-        "functionOptions": {
-          "match": "^[A-Z].*\\.$"
-        }
-      },
-      "tags": [
-        "operation"
-      ]
-    },
     "operation-tags": {
       "description": "Operation should have non-empty `tags` array.",
       "recommended": true,
@@ -423,6 +407,432 @@
       "then": {
         "function": "refSiblings"
       }
+    },
+    "oas2-api-host": {
+      "description": "OpenAPI `host` must be present and non-empty string.",
+      "recommended": true,
+      "formats": ["oas2"],
+      "type": "style",
+      "given": "$",
+      "then": {
+        "field": "host",
+        "function": "truthy"
+      },
+      "tags": [
+        "api"
+      ]
+    },
+    "oas2-api-schemes": {
+      "description": "OpenAPI host `schemes` must be present and non-empty array.",
+      "recommended": true,
+      "formats": ["oas2"],
+      "type": "style",
+      "given": "$",
+      "then": {
+        "field": "schemes",
+        "function": "schema",
+        "functionOptions": {
+          "schema": {
+            "items": {
+              "type": "string"
+            },
+            "minItems": 1,
+            "type": "array"
+          }
+        }
+      },
+      "tags": [
+        "api"
+      ]
+    },
+    "oas2-host-not-example": {
+      "description": "Host URL should not point at example.com.",
+      "recommended": false,
+      "formats": ["oas2"],
+      "given": "$",
+      "type": "style",
+      "then": {
+        "field": "host",
+        "function": "pattern",
+        "functionOptions": {
+          "notMatch": "example\\.com"
+        }
+      },
+      "tags": [
+        "api"
+      ]
+    },
+    "oas2-host-trailing-slash": {
+      "description": "Server URL should not have a trailing slash.",
+      "recommended": true,
+      "formats": ["oas2"],
+      "given": "$",
+      "type": "style",
+      "then": {
+        "field": "host",
+        "function": "pattern",
+        "functionOptions": {
+          "notMatch": "/$"
+        }
+      },
+      "tags": [
+        "api"
+      ]
+    },
+    "oas2-parameter-description": {
+      "description": "Parameter objects should have a `description`.",
+      "recommended": false,
+      "formats": ["oas2"],
+      "given": "$..[?(@parentProperty === 'parameters' && @.in)]",
+      "type": "style",
+      "then": {
+        "field": "description",
+        "function": "truthy"
+      },
+      "tags": [
+        "parameters"
+      ]
+    },
+    "oas2-operation-security-defined": {
+      "description": "Operation `security` values must match a scheme defined in the `securityDefinitions` object.",
+      "recommended": true,
+      "formats": ["oas2"],
+      "type": "validation",
+      "given": "$",
+      "then": {
+        "function": "oasOpSecurityDefined",
+        "functionOptions": {
+          "schemesPath": [
+            "securityDefinitions"
+          ]
+        }
+      },
+      "tags": [
+        "operation"
+      ]
+    },
+    "oas2-valid-parameter-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "recommended": true,
+      "formats": ["oas2"],
+      "severity": 0,
+      "type": "validation",
+      "given": "$..parameters..[?(@.in == 'body')]..[?(@property !== 'properties' && @.example && ( @.type || @.format || @.$ref ))]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$"
+        }
+      }
+    },
+    "oas2-valid-definition-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "recommended": true,
+      "formats": ["oas2"],
+      "severity": 0,
+      "type": "validation",
+      "given": "$..definitions..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$"
+        }
+      }
+    },
+    "oas2-anyOf": {
+      "description": "OpenAPI v3 keyword `anyOf` detected in OpenAPI v2 document.",
+      "message": "anyOf is not available in OpenAPI v2, it was added in OpenAPI v3",
+      "recommended": true,
+      "formats": ["oas2"],
+      "type": "validation",
+      "given": "$..[?(@.anyOf)]",
+      "then": {
+        "field": "anyOf",
+        "function": "undefined"
+      },
+      "tags": [
+        "schema"
+      ]
+    },
+    "oas2-oneOf": {
+      "description": "OpenAPI v3 keyword `oneOf` detected in OpenAPI v2 document.",
+      "message": "oneOf is not available in OpenAPI v2, it was added in OpenAPI v3",
+      "recommended": true,
+      "formats": ["oas2"],
+      "type": "validation",
+      "given": "$..[?(@.oneOf)]",
+      "then": {
+        "field": "oneOf",
+        "function": "undefined"
+      },
+      "tags": [
+        "schema"
+      ]
+    },
+    "oas2-schema": {
+      "description": "Validate structure of OpenAPI v2 specification.",
+      "message": "{{error}}",
+      "recommended": true,
+      "formats": ["oas2"],
+      "severity": 0,
+      "type": "validation",
+      "given": "$",
+      "then": {
+        "function": "schema",
+        "functionOptions": {
+          "schema": {
+            "$ref": "./schemas/schema.oas2.json"
+          }
+        }
+      },
+      "tags": [
+        "schema"
+      ]
+    },
+    "oas2-unused-definition": {
+      "description": "Potentially unused definition has been detected.",
+      "recommended": true,
+      "resolved": false,
+      "formats": ["oas2"],
+      "type": "style",
+      "given": "$.definitions",
+      "then": {
+        "function": "unreferencedReusableObject",
+        "functionOptions": {
+          "reusableObjectsLocation": "#/definitions"
+        }
+      }
+    },
+    "oas3-api-servers": {
+      "description": "OpenAPI `servers` must be present and non-empty array.",
+      "recommended": true,
+      "formats": ["oas3"],
+      "type": "style",
+      "given": "$",
+      "then": {
+        "field": "servers",
+        "function": "schema",
+        "functionOptions": {
+          "schema": {
+            "items": {
+              "type": "object"
+            },
+            "minItems": 1,
+            "type": "array"
+          }
+        }
+      },
+      "tags": [
+        "api"
+      ]
+    },
+    "oas3-operation-security-defined": {
+      "description": "Operation `security` values must match a scheme defined in the `components.securitySchemes` object.",
+      "recommended": true,
+      "formats": ["oas3"],
+      "type": "validation",
+      "given": "$",
+      "then": {
+        "function": "oasOpSecurityDefined",
+        "functionOptions": {
+          "schemesPath": [
+            "components",
+            "securitySchemes"
+          ]
+        }
+      },
+      "tags": [
+        "operation"
+      ]
+    },
+    "oas3-parameter-description": {
+      "description": "Parameter objects should have a `description`.",
+      "recommended": false,
+      "formats": ["oas3"],
+      "type": "style",
+      "given": "$..[?(@parentProperty !== 'links' && @.parameters)]['parameters'].[?(@.in)]",
+      "then": {
+        "field": "description",
+        "function": "truthy"
+      },
+      "tags": [
+        "parameters"
+      ]
+    },
+    "oas3-server-not-example.com": {
+      "description": "Server URL should not point at example.com.",
+      "recommended": false,
+      "formats": ["oas3"],
+      "type": "style",
+      "given": "$.servers[*].url",
+      "then": {
+        "function": "pattern",
+        "functionOptions": {
+          "notMatch": "example\\.com"
+        }
+      }
+    },
+    "oas3-server-trailing-slash": {
+      "description": "Server URL should not have a trailing slash.",
+      "recommended": true,
+      "formats": ["oas3"],
+      "type": "style",
+      "given": "$.servers[*].url",
+      "then": {
+        "function": "pattern",
+        "functionOptions": {
+          "notMatch": "/$"
+        }
+      }
+    },
+    "oas3-valid-oas-parameter-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "recommended": true,
+      "severity": 0,
+      "type": "validation",
+      "given": "$..parameters..[?(@.example && @.schema)]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$.schema"
+        }
+      }
+    },
+    "oas3-valid-oas-header-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "recommended": true,
+      "severity": 0,
+      "formats": ["oas3"],
+      "type": "validation",
+      "given": "$..headers..[?(@.example && @.schema)]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$.schema"
+        }
+      }
+    },
+    "oas3-valid-oas-content-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "recommended": true,
+      "severity": 0,
+      "formats": ["oas3"],
+      "type": "validation",
+      "given": "$..content..[?(@.example && @.schema)]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$.schema"
+        }
+      }
+    },
+    "oas3-valid-parameter-schema-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "recommended": true,
+      "severity": 0,
+      "formats": ["oas3"],
+      "type": "validation",
+      "given": "$..parameters..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$"
+        }
+      }
+    },
+    "oas3-valid-header-schema-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "recommended": true,
+      "severity": 0,
+      "formats": ["oas3"],
+      "type": "validation",
+      "given": "$..headers..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$"
+        }
+      }
+    },
+    "oas3-valid-schema-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "severity": 0,
+      "formats": ["oas3"],
+      "recommended": true,
+      "type": "validation",
+      "given": "$.components.schemas..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$"
+        }
+      }
+    },
+    "oas3-valid-content-schema-example": {
+      "description": "Examples must be valid against their defined schema.",
+      "message": "{{error}}",
+      "severity": 0,
+      "formats": ["oas3"],
+      "recommended": true,
+      "type": "validation",
+      "given": "$..content..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
+      "then": {
+        "function": "schemaPath",
+        "functionOptions": {
+          "field": "example",
+          "schemaPath": "$"
+        }
+      }
+    },
+    "oas3-schema": {
+      "description": "Validate structure of OpenAPI v3 specification.",
+      "message": "{{error}}",
+      "severity": 0,
+      "formats": ["oas3"],
+      "recommended": true,
+      "type": "validation",
+      "given": "$",
+      "then": {
+        "function": "schema",
+        "functionOptions": {
+          "schema": {
+            "$ref": "./schemas/schema.oas3.json"
+          }
+        }
+      },
+      "tags": [
+        "schema"
+      ]
+    },
+    "oas3-unused-components-schema": {
+      "description": "Potentially unused components schema has been detected.",
+      "recommended": true,
+      "formats": ["oas3"],
+      "type": "style",
+      "resolved": false,
+      "given": "$.components.schemas",
+      "then": {
+        "function": "unreferencedReusableObject",
+        "functionOptions": {
+          "reusableObjectsLocation": "#/components/schemas"
+        }
+      }
     }
   }
 }
diff --git a/src/rulesets/oas2/index.json b/src/rulesets/oas2/index.json
deleted file mode 100644
index 57eca2bc..00000000
--- a/src/rulesets/oas2/index.json
+++ /dev/null
@@ -1,197 +0,0 @@
-{
-  "extends": ["spectral:oas"],
-  "formats": ["oas2"],
-  "rules": {
-    "api-host": {
-      "description": "OpenAPI `host` must be present and non-empty string.",
-      "recommended": true,
-      "type": "style",
-      "given": "$",
-      "then": {
-        "field": "host",
-        "function": "truthy"
-      },
-      "tags": [
-        "api"
-      ]
-    },
-    "api-schemes": {
-      "description": "OpenAPI host `schemes` must be present and non-empty array.",
-      "recommended": true,
-      "type": "style",
-      "given": "$",
-      "then": {
-        "field": "schemes",
-        "function": "schema",
-        "functionOptions": {
-          "schema": {
-            "items": {
-              "type": "string"
-            },
-            "minItems": 1,
-            "type": "array"
-          }
-        }
-      },
-      "tags": [
-        "api"
-      ]
-    },
-    "host-not-example": {
-      "description": "Server URL should not point at `example.com`.",
-      "recommended": false,
-      "type": "style",
-      "given": "$",
-      "then": {
-        "field": "host",
-        "function": "pattern",
-        "functionOptions": {
-          "notMatch": "example\\.com"
-        }
-      }
-    },
-    "host-trailing-slash": {
-      "description": "Server URL should not have a trailing slash.",
-      "recommended": true,
-      "type": "style",
-      "given": "$",
-      "then": {
-        "field": "host",
-        "function": "pattern",
-        "functionOptions": {
-          "notMatch": "/$"
-        }
-      }
-    },
-    "definition-description": {
-      "description": "Definition `description` must be present and non-empty string.",
-      "recommended": false,
-      "type": "style",
-      "given": "$..definitions[*]",
-      "then": {
-        "field": "description",
-        "function": "truthy"
-      }
-    },
-    "oas2-parameter-description": {
-      "description": "Parameter objects should have a `description`.",
-      "recommended": false,
-      "type": "style",
-      "given": "$..[?(@parentProperty === 'parameters' && @.in)]",
-      "then": {
-        "field": "description",
-        "function": "truthy"
-      },
-      "tags": [
-        "parameters"
-      ]
-    },
-    "oas2-operation-security-defined": {
-      "description": "Operation `security` values must match a scheme defined in the `securityDefinitions` object.",
-      "recommended": true,
-      "type": "validation",
-      "given": "$",
-      "then": {
-        "function": "oasOpSecurityDefined",
-        "functionOptions": {
-          "schemesPath": [
-            "securityDefinitions"
-          ]
-        }
-      },
-      "tags": [
-        "operation"
-      ]
-    },
-    "valid-example-in-parameters": {
-      "description": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "recommended": true,
-      "severity": 0,
-      "type": "validation",
-      "given": "$..parameters..[?(@.in == 'body')]..[?(@property !== 'properties' && @.example && ( @.type || @.format || @.$ref ))]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$"
-        }
-      }
-    },
-    "valid-example-in-definitions": {
-      "description": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "recommended": true,
-      "severity": 0,
-      "type": "validation",
-      "given": "$..definitions..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$"
-        }
-      }
-    },
-    "oas2-anyOf": {
-      "description": "OpenAPI v3 keyword `anyOf` detected in OpenAPI v2 document.",
-      "message": "anyOf is not available in OpenAPI v2, it was added in OpenAPI v3",
-      "recommended": true,
-      "type": "validation",
-      "given": "$..[?(@.anyOf)]",
-      "then": {
-        "field": "anyOf",
-        "function": "undefined"
-      },
-      "tags": [
-        "schema"
-      ]
-    },
-    "oas2-oneOf": {
-      "description": "OpenAPI v3 keyword `oneOf` detected in OpenAPI v2 document.",
-      "message": "oneOf is not available in OpenAPI v2, it was added in OpenAPI v3",
-      "recommended": true,
-      "type": "validation",
-      "given": "$..[?(@.oneOf)]",
-      "then": {
-        "field": "oneOf",
-        "function": "undefined"
-      },
-      "tags": [
-        "schema"
-      ]
-    },
-    "oas2-schema": {
-      "description": "Validate structure of OpenAPI v2 specification.",
-      "message": "{{error}}",
-      "type": "validation",
-      "severity": 0,
-      "recommended": true,
-      "given": "$",
-      "then": {
-        "function": "schema",
-        "functionOptions": {
-          "schema": {
-            "$ref": "./schemas/main.json"
-          }
-        }
-      },
-      "tags": [
-        "schema"
-      ]
-    },
-    "unused-definition": {
-      "description": "Potentially unused definition has been detected.",
-      "recommended": true,
-      "type": "style",
-      "resolved": false,
-      "given": "$.definitions",
-      "then": {
-        "function": "unreferencedReusableObject",
-        "functionOptions": {
-          "reusableObjectsLocation": "#/definitions"
-        }
-      }
-    }
-  }
-}
diff --git a/src/rulesets/oas3/index.json b/src/rulesets/oas3/index.json
deleted file mode 100644
index aa070b6f..00000000
--- a/src/rulesets/oas3/index.json
+++ /dev/null
@@ -1,232 +0,0 @@
-{
-  "extends": ["spectral:oas"],
-  "formats": ["oas3"],
-  "rules": {
-    "api-servers": {
-      "description": "OpenAPI `servers` must be present and non-empty array.",
-      "recommended": true,
-      "type": "style",
-      "given": "$",
-      "then": {
-        "field": "servers",
-        "function": "schema",
-        "functionOptions": {
-          "schema": {
-            "items": {
-              "type": "object"
-            },
-            "minItems": 1,
-            "type": "array"
-          }
-        }
-      },
-      "tags": [
-        "api"
-      ]
-    },
-    "components-schema-description": {
-      "description": "Model `description` must be present and non-empty string.",
-      "recommended": false,
-      "type": "style",
-      "given": "$.components.schemas[*]",
-      "then": {
-        "field": "description",
-        "function": "truthy"
-      }
-    },
-    "oas3-operation-security-defined": {
-      "description": "Operation `security` values must match a scheme defined in the `components.securitySchemes` object.",
-      "recommended": true,
-      "type": "validation",
-      "given": "$",
-      "then": {
-        "function": "oasOpSecurityDefined",
-        "functionOptions": {
-          "schemesPath": [
-            "components",
-            "securitySchemes"
-          ]
-        }
-      },
-      "tags": [
-        "operation"
-      ]
-    },
-    "oas3-parameter-description": {
-      "description": "Parameter objects should have a `description`.",
-      "recommended": false,
-      "type": "style",
-      "given": "$..[?(@parentProperty !== 'links' && @.parameters)]['parameters'].[?(@.in)]",
-      "then": {
-        "field": "description",
-        "function": "truthy"
-      },
-      "tags": [
-        "parameters"
-      ]
-    },
-    "server-not-example.com": {
-      "description": "Server URL should not point at `example.com`.",
-      "recommended": false,
-      "type": "style",
-      "given": "$.servers[*]",
-      "then": {
-        "field": "url",
-        "function": "pattern",
-        "functionOptions": {
-          "notMatch": "example\\.com"
-        }
-      }
-    },
-    "server-trailing-slash": {
-      "description": "Server URL should not have a trailing slash.",
-      "recommended": true,
-      "type": "style",
-      "given": "$.servers[*]",
-      "then": {
-        "field": "url",
-        "function": "pattern",
-        "functionOptions": {
-          "notMatch": "/$"
-        }
-      }
-    },
-    "valid-oas-example-in-parameters": {
-      "description": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "recommended": true,
-      "severity": 0,
-      "type": "validation",
-      "given": "$..parameters..[?(@.example && @.schema)]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$.schema"
-        }
-      }
-    },
-    "valid-oas-example-in-headers": {
-      "description": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "recommended": true,
-      "severity": 0,
-      "type": "validation",
-      "given": "$..headers..[?(@.example && @.schema)]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$.schema"
-        }
-      }
-    },
-    "valid-oas-example-in-content": {
-      "description": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "recommended": true,
-      "severity": 0,
-      "type": "validation",
-      "given": "$..content..[?(@.example && @.schema)]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$.schema"
-        }
-      }
-    },
-    "valid-schema-example-in-parameters": {
-      "summary": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "recommended": true,
-      "severity": 0,
-      "type": "validation",
-      "given": "$..parameters..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$"
-        }
-      }
-    },
-    "valid-schema-example-in-headers": {
-      "summary": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "severity": 0,
-      "recommended": true,
-      "type": "validation",
-      "given": "$..headers..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$"
-        }
-      }
-    },
-    "valid-example-in-schemas": {
-      "summary": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "severity": 0,
-      "recommended": true,
-      "type": "validation",
-      "given": "$.components.schemas..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$"
-        }
-      }
-    },
-    "valid-schema-example-in-content": {
-      "summary": "Examples must be valid against their defined schema.",
-      "message": "{{error}}",
-      "severity": 0,
-      "recommended": true,
-      "type": "validation",
-      "given": "$..content..[?(@property !== 'properties' && @.example && (@.type || @.format || @.$ref))]",
-      "then": {
-        "function": "schemaPath",
-        "functionOptions": {
-          "field": "example",
-          "schemaPath": "$"
-        }
-      }
-    },
-    "oas3-schema": {
-      "description": "Validate structure of OpenAPI v3 specification.",
-      "message": "{{error}}",
-      "type": "validation",
-      "severity": 0,
-      "recommended": true,
-      "given": "$",
-      "then": {
-        "function": "schema",
-        "functionOptions": {
-          "schema": {
-            "$ref": "./schemas/main.json"
-          }
-        }
-      },
-      "tags": [
-        "schema"
-      ]
-    },
-    "unused-components-schema": {
-      "description": "Potentially unused components schema has been detected.",
-      "recommended": true,
-      "type": "style",
-      "resolved": false,
-      "given": "$.components.schemas",
-      "then": {
-        "function": "unreferencedReusableObject",
-        "functionOptions": {
-          "reusableObjectsLocation": "#/components/schemas"
-        }
-      }
-    }
-  }
-}


Test patch:
diff --git a/src/__tests__/__fixtures__/bare-oas-ruleset.json b/src/__tests__/__fixtures__/bare-oas-ruleset.json
new file mode 100644
index 00000000..3c980184
--- /dev/null
+++ b/src/__tests__/__fixtures__/bare-oas-ruleset.json
@@ -0,0 +1,17 @@
+{
+  "rules": {
+    "info-matches-stoplight": {
+      "message": "Info must contain Stoplight",
+      "given": "$.info",
+      "type": "style",
+      "recommended": true,
+      "then": {
+        "field": "title",
+        "function": "pattern",
+        "functionOptions": {
+          "match": "Stoplight"
+        }
+      }
+    }
+  }
+}
diff --git a/src/__tests__/linter.test.ts b/src/__tests__/linter.test.ts
index 90125ca3..32353412 100644
--- a/src/__tests__/linter.test.ts
+++ b/src/__tests__/linter.test.ts
@@ -144,12 +144,12 @@ describe('linter', () => {
   });
 
   test('should not report anything for disabled rules', async () => {
-    await spectral.loadRuleset('spectral:oas3');
-    const { rules: oas3Rules } = await readRuleset('spectral:oas3');
+    await spectral.loadRuleset('spectral:oas');
+    const { rules: oasRules } = await readRuleset('spectral:oas');
     spectral.setRules(
-      mergeRules(oas3Rules, {
-        'valid-example-in-schemas': 'off',
-        'components-schema-description': -1,
+      mergeRules(oasRules, {
+        'oas3-valid-schema-example': 'off',
+        'operation-2xx-response': -1,
         'openapi-tags': 'off',
       }) as RuleCollection,
     );
@@ -169,12 +169,12 @@ describe('linter', () => {
         path: ['paths', '/pets', 'get', 'responses', '200'],
       }),
       expect.objectContaining({
-        code: 'unused-components-schema',
+        code: 'oas3-unused-components-schema',
         message: 'Potentially unused components schema has been detected.',
         path: ['components', 'schemas', 'Pets'],
       }),
       expect.objectContaining({
-        code: 'unused-components-schema',
+        code: 'oas3-unused-components-schema',
         message: 'Potentially unused components schema has been detected.',
         path: ['components', 'schemas', 'foo'],
       }),
@@ -182,7 +182,7 @@ describe('linter', () => {
   });
 
   test('should output unescaped json paths', async () => {
-    await spectral.loadRuleset('spectral:oas3');
+    await spectral.loadRuleset('spectral:oas');
 
     const result = await spectral.run(invalidSchema);
 
@@ -459,7 +459,7 @@ describe('linter', () => {
   });
 
   test('should include parser diagnostics', async () => {
-    await spectral.loadRuleset('spectral:oas2');
+    await spectral.loadRuleset('spectral:oas');
 
     const responses = `openapi: 2.0.0
 responses:: !!foo
@@ -512,7 +512,7 @@ responses:: !!foo
   });
 
   test('should report a valid line number for json paths containing escaped slashes', async () => {
-    await spectral.loadRuleset('spectral:oas3');
+    await spectral.loadRuleset('spectral:oas');
 
     const result = await spectral.run(studioFixture);
 
@@ -537,7 +537,7 @@ responses:: !!foo
   });
 
   test('should remove all redundant ajv errors', async () => {
-    await spectral.loadRuleset('spectral:oas3');
+    await spectral.loadRuleset('spectral:oas');
 
     const result = await spectral.run(invalidSchema);
 
@@ -555,7 +555,7 @@ responses:: !!foo
         code: 'openapi-tags',
       }),
       expect.objectContaining({
-        code: 'valid-example-in-schemas',
+        code: 'oas3-valid-schema-example',
         message: '"foo.example" property type should be number',
         path: ['components', 'schemas', 'foo', 'example'],
       }),
@@ -565,12 +565,12 @@ responses:: !!foo
         path: ['paths', '/pets', 'get', 'responses', '200'],
       }),
       expect.objectContaining({
-        code: 'unused-components-schema',
+        code: 'oas3-unused-components-schema',
         message: 'Potentially unused components schema has been detected.',
         path: ['components', 'schemas', 'Pets'],
       }),
       expect.objectContaining({
-        code: 'unused-components-schema',
+        code: 'oas3-unused-components-schema',
         message: 'Potentially unused components schema has been detected.',
         path: ['components', 'schemas', 'foo'],
       }),
@@ -578,14 +578,14 @@ responses:: !!foo
   });
 
   test('should report invalid schema $refs', async () => {
-    await spectral.loadRuleset('spectral:oas2');
+    await spectral.loadRuleset('spectral:oas');
 
     const result = await spectral.run(todosInvalid);
 
     expect(result).toEqual(
       expect.arrayContaining([
         expect.objectContaining({
-          code: 'valid-example-in-parameters',
+          code: 'oas2-valid-parameter-example',
           message: '"schema.example" property can\'t resolve reference #/parameters/missing from id #',
           path: ['paths', '/todos/{todoId}', 'put', 'parameters', '1', 'schema', 'example'],
         }),
@@ -594,7 +594,7 @@ responses:: !!foo
   });
 
   test('should report invalid $refs', async () => {
-    await spectral.loadRuleset('spectral:oas3');
+    await spectral.loadRuleset('spectral:oas');
 
     const result = await spectral.run(invalidSchema);
 
@@ -617,7 +617,7 @@ responses:: !!foo
   });
 
   test('should support YAML merge keys', async () => {
-    await spectral.loadRuleset('spectral:oas3');
+    await spectral.loadRuleset('spectral:oas');
     spectral.setRules({
       'operation-tag-defined': {
         ...spectral.rules['operation-tag-defined'],
@@ -914,7 +914,7 @@ responses:: !!foo
   });
 
   test('should evaluate {{path}} in validation messages', async () => {
-    await spectral.loadRuleset('spectral:oas3');
+    await spectral.loadRuleset('spectral:oas');
     spectral.setRules({
       'oas3-schema': {
         ...spectral.rules['oas3-schema'],
diff --git a/src/__tests__/spectral.jest.test.ts b/src/__tests__/spectral.jest.test.ts
index 056c0db8..8ed9a12a 100644
--- a/src/__tests__/spectral.jest.test.ts
+++ b/src/__tests__/spectral.jest.test.ts
@@ -78,6 +78,25 @@ describe('Spectral', () => {
     });
   });
 
+  test('should support combining built-in ruleset with a custom one', async () => {
+    const s = new Spectral();
+    await s.loadRuleset(['spectral:oas', path.join(__dirname, './__fixtures__/bare-oas-ruleset.json')]);
+
+    expect(Object.values(s.rules)).toEqual(
+      expect.arrayContaining([
+        expect.objectContaining({
+          name: 'oas2-schema',
+        }),
+        expect.objectContaining({
+          name: 'oas3-schema',
+        }),
+        expect.objectContaining({
+          name: 'info-matches-stoplight',
+        }),
+      ]),
+    );
+  });
+
   test('reports issues for correct files with correct ranges and paths', async () => {
     const documentUri = path.join(__dirname, './__fixtures__/document-with-external-refs.oas2.json');
     const spectral = new Spectral({ resolver: httpAndFileResolver });
diff --git a/src/__tests__/spectral.test.ts b/src/__tests__/spectral.test.ts
index ac26814c..ba09b745 100644
--- a/src/__tests__/spectral.test.ts
+++ b/src/__tests__/spectral.test.ts
@@ -8,24 +8,17 @@ import { IParsedResult, IResolver, IRunRule, RuleFunction } from '../types';
 const merge = require('lodash/merge');
 
 const oasRuleset = JSON.parse(JSON.stringify(require('../rulesets/oas/index.json')));
-const oas2Ruleset = JSON.parse(JSON.stringify(require('../rulesets/oas2/index.json')));
-const oas3Ruleset = JSON.parse(JSON.stringify(require('../rulesets/oas3/index.json')));
-
 const oasRulesetRules: Dictionary<IRunRule, string> = oasRuleset.rules;
-const oas2RulesetRules: Dictionary<IRunRule, string> = oas2Ruleset.rules;
-const oas3RulesetRules: Dictionary<IRunRule, string> = oas3Ruleset.rules;
 
 describe('spectral', () => {
   describe('loadRuleset', () => {
     test('should support loading built-in rulesets', async () => {
       const s = new Spectral();
-      await s.loadRuleset('spectral:oas2');
+      await s.loadRuleset('spectral:oas');
 
       expect(s.rules).toEqual(
         expect.objectContaining(
-          [...Object.entries(oasRulesetRules), ...Object.entries(oas2RulesetRules)].reduce<
-            Dictionary<IRunRule, string>
-          >((oasRules, [name, rule]) => {
+          Object.entries(oasRulesetRules).reduce<Dictionary<IRunRule, string>>((oasRules, [name, rule]) => {
             oasRules[name] = {
               name,
               ...rule,
@@ -40,16 +33,12 @@ describe('spectral', () => {
       );
     });
 
-    test('should support loading multiple built-in rulesets', async () => {
+    test('should support loading multiple times the built-in ruleset', async () => {
       const s = new Spectral();
-      await s.loadRuleset(['spectral:oas2', 'spectral:oas3']);
+      await s.loadRuleset(['spectral:oas', 'spectral:oas']);
 
       expect(s.rules).toEqual(
-        [
-          ...Object.entries(oasRulesetRules),
-          ...Object.entries(oas2RulesetRules),
-          ...Object.entries(oas3RulesetRules),
-        ].reduce<Dictionary<IRunRule, string>>((oasRules, [name, rule]) => {
+        Object.entries(oasRulesetRules).reduce<Dictionary<IRunRule, string>>((oasRules, [name, rule]) => {
           oasRules[name] = {
             name,
             ...rule,
@@ -68,7 +57,6 @@ describe('spectral', () => {
     test('should not mutate the passing in rules object', () => {
       const givenCustomRuleSet = {
         rule1: {
-          summary: '',
           given: '$',
           then: {
             function: RuleFunction.TRUTHY,
diff --git a/src/cli/services/__tests__/linter.test.ts b/src/cli/services/__tests__/linter.test.ts
index 3a28748a..7a5cfbcd 100644
--- a/src/cli/services/__tests__/linter.test.ts
+++ b/src/cli/services/__tests__/linter.test.ts
@@ -14,8 +14,7 @@ const invalidRulesetPath = resolve(__dirname, '__fixtures__/ruleset-invalid.yaml
 const validRulesetPath = resolve(__dirname, '__fixtures__/ruleset-valid.yaml');
 const validNestedRulesetPath = resolve(__dirname, '__fixtures__/ruleset-extends-valid.yaml');
 const invalidNestedRulesetPath = resolve(__dirname, '__fixtures__/ruleset-extends-invalid.yaml');
-const standardOas3RulesetPath = resolve(__dirname, '../../../rulesets/oas3/index.json');
-const standardOas2RulesetPath = resolve(__dirname, '../../../rulesets/oas2/index.json');
+const standardOasRulesetPath = resolve(__dirname, '../../../rulesets/oas/index.json');
 const draftRefSpec = resolve(__dirname, './__fixtures__/draft-ref.oas2.json');
 const draftNestedRefSpec = resolve(__dirname, './__fixtures__/draft-nested-ref.oas2.json');
 const validOas3SpecPath = resolve(__dirname, './__fixtures__/openapi-3.0-valid.yaml');
@@ -120,7 +119,7 @@ describe('Linter service', () => {
         expect(logSpy).toHaveBeenCalledWith('OpenAPI 3.x detected');
         expect(output).toEqual(
           expect.arrayContaining([
-            expect.objectContaining({ code: 'api-servers' }),
+            expect.objectContaining({ code: 'oas3-api-servers' }),
             expect.objectContaining({ code: 'info-contact' }),
           ]),
         );
@@ -130,17 +129,17 @@ describe('Linter service', () => {
         it('output other warnings but not info-contact', async () => {
           const output = await run(`lint --skip-rule=info-contact ${document}`);
 
-          expect(output).toEqual(expect.arrayContaining([expect.objectContaining({ code: 'api-servers' })]));
+          expect(output).toEqual(expect.arrayContaining([expect.objectContaining({ code: 'oas3-api-servers' })]));
           expect(output).toEqual(expect.not.arrayContaining([expect.objectContaining({ code: 'info-contact' })]));
         });
       });
 
-      describe('and --skip-rule=info-contact --skip-rule=api-servers is set', () => {
-        it('outputs neither info-contact or api-servers', async () => {
-          const output = await run(`lint --skip-rule=info-contact --skip-rule=api-servers ${document}`);
+      describe('and --skip-rule=info-contact --skip-rule=oas3-api-servers is set', () => {
+        it('outputs neither info-contact or oas3-api-servers', async () => {
+          const output = await run(`lint --skip-rule=info-contact --skip-rule=oas3-api-servers ${document}`);
 
           expect(output).toEqual(expect.not.arrayContaining([expect.objectContaining({ code: 'info-contact' })]));
-          expect(output).toEqual(expect.not.arrayContaining([expect.objectContaining({ code: 'api-servers' })]));
+          expect(output).toEqual(expect.not.arrayContaining([expect.objectContaining({ code: 'oas3-api-servers' })]));
         });
       });
     });
@@ -167,7 +166,7 @@ describe('Linter service', () => {
             source: join(process.cwd(), 'src/__tests__/__fixtures__/petstore.invalid-schema.oas3.json'),
           }),
           expect.objectContaining({
-            code: 'valid-example-in-schemas',
+            code: 'oas3-valid-schema-example',
             path: ['components', 'schemas', 'foo', 'example'],
             source: join(process.cwd(), 'src/__tests__/__fixtures__/petstore.invalid-schema.oas3.json'),
           }),
@@ -219,7 +218,7 @@ describe('Linter service', () => {
             source: join(process.cwd(), 'src/__tests__/__fixtures__/petstore.invalid-schema.oas3.json'),
           }),
           expect.objectContaining({
-            code: 'valid-example-in-schemas',
+            code: 'oas3-valid-schema-example',
             path: ['components', 'schemas', 'foo', 'example'],
             source: join(process.cwd(), 'src/__tests__/__fixtures__/petstore.invalid-schema.oas3.json'),
           }),
@@ -386,12 +385,12 @@ describe('Linter service', () => {
             source: join(process.cwd(), 'src/__tests__/__fixtures__/petstore.invalid-schema.oas3.json'),
           }),
           expect.objectContaining({
-            code: 'valid-example-in-schemas',
+            code: 'oas3-valid-schema-example',
             path: ['components', 'schemas', 'foo', 'example'],
             source: join(process.cwd(), 'src/__tests__/__fixtures__/petstore.invalid-schema.oas3.json'),
           }),
           expect.objectContaining({
-            code: 'unused-components-schema',
+            code: 'oas3-unused-components-schema',
             path: ['components', 'schemas', 'Pets'],
             source: join(process.cwd(), 'src/__tests__/__fixtures__/petstore.invalid-schema.oas3.json'),
           }),
@@ -481,9 +480,9 @@ describe('Linter service', () => {
 
       describe('when a standard oas3 ruleset provided through option', () => {
         it('outputs warnings', () => {
-          return expect(run(`lint ${invalidOas3SpecPath} -r ${standardOas3RulesetPath}`)).resolves.toEqual(
+          return expect(run(`lint ${invalidOas3SpecPath} -r ${standardOasRulesetPath}`)).resolves.toEqual(
             expect.arrayContaining([
-              expect.objectContaining({ code: 'api-servers' }),
+              expect.objectContaining({ code: 'oas3-api-servers' }),
               expect.objectContaining({ code: 'info-contact' }),
               expect.objectContaining({ code: 'info-description' }),
             ]),
@@ -493,7 +492,7 @@ describe('Linter service', () => {
 
       describe('when a standard oas2 ruleset provided through option', () => {
         it('outputs warnings', async () => {
-          const output = await run(`lint ${oas2PetstoreSpecPath} -r ${standardOas2RulesetPath}`);
+          const output = await run(`lint ${oas2PetstoreSpecPath} -r ${standardOasRulesetPath}`);
           expect(output).toEqual(expect.arrayContaining([expect.objectContaining({ code: 'operation-description' })]));
           expect(output).toHaveLength(22);
         });
@@ -559,7 +558,7 @@ describe('Linter service', () => {
     it('outputs errors occurring in referenced files', () => {
       return expect(run(`lint ${draftRefSpec}`)).resolves.toEqual([
         expect.objectContaining({
-          code: 'api-schemes',
+          code: 'oas2-api-schemes',
           message: 'OpenAPI host `schemes` must be present and non-empty array.',
           path: [],
           range: expect.any(Object),
@@ -586,7 +585,7 @@ describe('Linter service', () => {
               line: 3,
             },
           },
-          source: expect.stringContaining('__tests__/__fixtures__/refs/info.json'),
+          source: expect.stringContaining('/__tests__/__fixtures__/refs/info.json'),
         }),
         expect.objectContaining({
           code: 'info-description',
@@ -610,7 +609,7 @@ describe('Linter service', () => {
     it('outputs errors occurring in nested referenced files', () => {
       return expect(run(`lint ${draftNestedRefSpec}`)).resolves.toEqual([
         expect.objectContaining({
-          code: 'api-schemes',
+          code: 'oas2-api-schemes',
           message: 'OpenAPI host `schemes` must be present and non-empty array.',
           path: [],
           range: expect.any(Object),
@@ -711,7 +710,7 @@ describe('Linter service', () => {
     it('uses provided resolver for $ref resolving', async () => {
       expect(await run(`lint --resolver ${fooResolver} ${fooDocument}`)).toEqual([
         expect.objectContaining({
-          code: 'api-servers',
+          code: 'oas3-api-servers',
         }),
         expect.objectContaining({
           code: 'openapi-tags',
diff --git a/src/rulesets/__tests__/__fixtures__/custom-oas-ruleset.json b/src/rulesets/__tests__/__fixtures__/custom-oas-ruleset.json
new file mode 100644
index 00000000..feaa37e0
--- /dev/null
+++ b/src/rulesets/__tests__/__fixtures__/custom-oas-ruleset.json
@@ -0,0 +1,12 @@
+{
+  "formats": ["oas2", "oas3"],
+  "rules": {
+    "generic-valid-rule": {
+      "message": "should be OK",
+      "given": "$.info",
+      "then": {
+        "function": "truthy"
+      }
+    }
+  }
+}
diff --git a/src/rulesets/__tests__/__fixtures__/custom-oas2-ruleset.json b/src/rulesets/__tests__/__fixtures__/custom-oas2-ruleset.json
new file mode 100644
index 00000000..de8094de
--- /dev/null
+++ b/src/rulesets/__tests__/__fixtures__/custom-oas2-ruleset.json
@@ -0,0 +1,12 @@
+{
+  "formats": ["oas2"],
+  "rules": {
+    "oas2-valid-rule": {
+      "message": "should be OK",
+      "given": "$.info",
+      "then": {
+        "function": "truthy"
+      }
+    }
+  }
+}
diff --git a/src/rulesets/__tests__/__fixtures__/custom-oas3-ruleset.json b/src/rulesets/__tests__/__fixtures__/custom-oas3-ruleset.json
new file mode 100644
index 00000000..82714db5
--- /dev/null
+++ b/src/rulesets/__tests__/__fixtures__/custom-oas3-ruleset.json
@@ -0,0 +1,12 @@
+{
+  "formats": ["oas3"],
+  "rules": {
+    "oas3-valid-rule": {
+      "message": "should be OK",
+      "given": "$.info",
+      "then": {
+        "function": "truthy"
+      }
+    }
+  }
+}
diff --git a/src/rulesets/__tests__/__fixtures__/enable-all-ruleset.json b/src/rulesets/__tests__/__fixtures__/enable-all-ruleset.json
index f1feac77..70b11ff3 100644
--- a/src/rulesets/__tests__/__fixtures__/enable-all-ruleset.json
+++ b/src/rulesets/__tests__/__fixtures__/enable-all-ruleset.json
@@ -1,6 +1,6 @@
 {
   "extends": [[
-    "./extends-disabled-oas2-ruleset.yaml", "all"
+    "./extends-disabled-oas-ruleset.yaml", "all"
   ]],
   "rules": {}
 }
diff --git a/src/rulesets/__tests__/__fixtures__/extends-disabled-oas2-ruleset.yaml b/src/rulesets/__tests__/__fixtures__/extends-disabled-oas-ruleset.yaml
similarity index 57%
rename from src/rulesets/__tests__/__fixtures__/extends-disabled-oas2-ruleset.yaml
rename to src/rulesets/__tests__/__fixtures__/extends-disabled-oas-ruleset.yaml
index dded304e..af31524b 100644
--- a/src/rulesets/__tests__/__fixtures__/extends-disabled-oas2-ruleset.yaml
+++ b/src/rulesets/__tests__/__fixtures__/extends-disabled-oas-ruleset.yaml
@@ -1,3 +1,3 @@
-extends: [[spectral:oas2, off]]
+extends: [[spectral:oas, off]]
 rules:
   operation-operationId-unique: true
diff --git a/src/rulesets/__tests__/__fixtures__/extends-oas2-ruleset.json b/src/rulesets/__tests__/__fixtures__/extends-oas-ruleset.json
similarity index 79%
rename from src/rulesets/__tests__/__fixtures__/extends-oas2-ruleset.json
rename to src/rulesets/__tests__/__fixtures__/extends-oas-ruleset.json
index 66efb24d..e59b7032 100644
--- a/src/rulesets/__tests__/__fixtures__/extends-oas2-ruleset.json
+++ b/src/rulesets/__tests__/__fixtures__/extends-oas-ruleset.json
@@ -1,5 +1,5 @@
 {
-  "extends": [["spectral:oas2", "all"]],
+  "extends": [["spectral:oas", "all"]],
   "rules": {
     "valid-rule": {
       "message": "should be OK",
diff --git a/src/rulesets/__tests__/__fixtures__/extends-oas2-with-override-ruleset.json b/src/rulesets/__tests__/__fixtures__/extends-oas-with-override-ruleset.json
similarity index 86%
rename from src/rulesets/__tests__/__fixtures__/extends-oas2-with-override-ruleset.json
rename to src/rulesets/__tests__/__fixtures__/extends-oas-with-override-ruleset.json
index f0a0db08..47b6f856 100644
--- a/src/rulesets/__tests__/__fixtures__/extends-oas2-with-override-ruleset.json
+++ b/src/rulesets/__tests__/__fixtures__/extends-oas-with-override-ruleset.json
@@ -1,5 +1,5 @@
 {
-  "extends": [["spectral:oas2", "all"]],
+  "extends": [["spectral:oas", "all"]],
   "rules": {
     "oas2-operation-security-defined": "off",
     "operation-2xx-response": {
diff --git a/src/rulesets/__tests__/__fixtures__/extends-unspecified-oas2-ruleset.json b/src/rulesets/__tests__/__fixtures__/extends-unspecified-oas-ruleset.json
similarity index 83%
rename from src/rulesets/__tests__/__fixtures__/extends-unspecified-oas2-ruleset.json
rename to src/rulesets/__tests__/__fixtures__/extends-unspecified-oas-ruleset.json
index 6df464f1..a1cb1cc4 100644
--- a/src/rulesets/__tests__/__fixtures__/extends-unspecified-oas2-ruleset.json
+++ b/src/rulesets/__tests__/__fixtures__/extends-unspecified-oas-ruleset.json
@@ -1,5 +1,5 @@
 {
-  "extends": ["spectral:oas2"],
+  "extends": ["spectral:oas"],
   "rules": {
     "valid-rule": {
       "message": "should be OK",
diff --git a/src/rulesets/__tests__/__fixtures__/github-issue-447-fixture.yaml b/src/rulesets/__tests__/__fixtures__/github-issue-447-fixture.yaml
index 3211964c..40946401 100644
--- a/src/rulesets/__tests__/__fixtures__/github-issue-447-fixture.yaml
+++ b/src/rulesets/__tests__/__fixtures__/github-issue-447-fixture.yaml
@@ -1,4 +1,4 @@
-extends: spectral:oas3
+extends: spectral:oas
 rules:
   operation-operationId: error
   operation-tags: hint
diff --git a/src/rulesets/__tests__/__fixtures__/my-open-api-ruleset.json b/src/rulesets/__tests__/__fixtures__/my-open-api-ruleset.json
index dbbcf337..0a2cf89b 100644
--- a/src/rulesets/__tests__/__fixtures__/my-open-api-ruleset.json
+++ b/src/rulesets/__tests__/__fixtures__/my-open-api-ruleset.json
@@ -1,7 +1,11 @@
 {
-  "extends": ["spectral:oas2", "spectral:oas3"],
+  "extends": [
+    "./custom-oas-ruleset.json",
+    "./custom-oas2-ruleset.json",
+    "./custom-oas3-ruleset.json"
+  ],
   "rules": {
-    "valid-rule": {
+    "my-valid-rule": {
       "message": "should be OK",
       "given": "$.info",
       "then": {
diff --git a/src/rulesets/__tests__/finder.jest.test.ts b/src/rulesets/__tests__/finder.jest.test.ts
index 77d36e90..e1f32101 100644
--- a/src/rulesets/__tests__/finder.jest.test.ts
+++ b/src/rulesets/__tests__/finder.jest.test.ts
@@ -23,25 +23,22 @@ describe('Rulesets finder', () => {
   });
 
   it('should support spectral built-in rules', () => {
-    return expect(findFile('/b/c/d', '@stoplight/spectral/rulesets/oas2/index.json')).resolves.toEqual(
-      path.join(process.cwd(), 'src/rulesets/oas2/index.json'),
+    return expect(findFile('/b/c/d', '@stoplight/spectral/rulesets/oas/index.json')).resolves.toEqual(
+      path.join(process.cwd(), 'src/rulesets/oas/index.json'),
     );
   });
 
-  it.each(['oas', 'oas2', 'oas3'])('should support spectral built-in %s ruleset shorthand', shorthand => {
-    return expect(findFile('', `spectral:${shorthand}`)).resolves.toEqual(
-      path.join(process.cwd(), `src/rulesets/${shorthand}/index.json`),
+  it('should support spectral built-in ruleset shorthand', () => {
+    return expect(findFile('', `spectral:oas`)).resolves.toEqual(
+      path.join(process.cwd(), `src/rulesets/oas/index.json`),
     );
   });
 
-  it.each(['oas', 'oas2', 'oas3'])(
-    'should resolve spectral built-in %s ruleset shorthand even if a base uri is provided',
-    shorthand => {
-      return expect(findFile('https://localhost:4000', `spectral:${shorthand}`)).resolves.toEqual(
-        path.join(process.cwd(), `src/rulesets/${shorthand}/index.json`),
-      );
-    },
-  );
+  it('should resolve spectral built-in ruleset shorthand even if a base uri is provided', () => {
+    return expect(findFile('https://localhost:4000', `spectral:oas`)).resolves.toEqual(
+      path.join(process.cwd(), `src/rulesets/oas/index.json`),
+    );
+  });
 
   it('should load local npm module if available', () => {
     return expect(findFile('', '@stoplight/json')).resolves.toEqual(require.resolve('@stoplight/json'));
diff --git a/src/rulesets/__tests__/finder.karma.test.ts b/src/rulesets/__tests__/finder.karma.test.ts
index c5a67bcf..bcaecb1a 100644
--- a/src/rulesets/__tests__/finder.karma.test.ts
+++ b/src/rulesets/__tests__/finder.karma.test.ts
@@ -2,22 +2,20 @@ import { findFile } from '../finder';
 
 describe('Rulesets finder', () => {
   it('should point to unpkg.com if npm module', () => {
-    return expect(findFile('', '@stoplight/spectral/rulesets/oas2/index.json')).resolves.toEqual(
-      'https://unpkg.com/@stoplight/spectral/rulesets/oas2/index.json',
+    return expect(findFile('', '@stoplight/spectral/rulesets/oas/index.json')).resolves.toEqual(
+      'https://unpkg.com/@stoplight/spectral/rulesets/oas/index.json',
     );
   });
 
-  for (const shorthand of ['oas', 'oas2', 'oas3']) {
-    it(`should support spectral built-in ${shorthand} ruleset shorthand`, () => {
-      return expect(findFile('', `spectral:${shorthand}`)).resolves.toEqual(
-        `https://unpkg.com/@stoplight/spectral/rulesets/${shorthand}/index.json`,
-      );
-    });
+  it(`should support spectral built-in ruleset shorthand`, () => {
+    return expect(findFile('', `spectral:oas`)).resolves.toEqual(
+      `https://unpkg.com/@stoplight/spectral/rulesets/oas/index.json`,
+    );
+  });
 
-    it(`should resolve spectral built-in ${shorthand} ruleset shorthand even if a base uri is provided`, () => {
-      return expect(findFile('https://localhost:4000', `spectral:${shorthand}`)).resolves.toEqual(
-        `https://unpkg.com/@stoplight/spectral/rulesets/${shorthand}/index.json`,
-      );
-    });
-  }
+  it(`should resolve spectral built-in ruleset shorthand even if a base uri is provided`, () => {
+    return expect(findFile('https://localhost:4000', `spectral:oas`)).resolves.toEqual(
+      `https://unpkg.com/@stoplight/spectral/rulesets/oas/index.json`,
+    );
+  });
 });
diff --git a/src/rulesets/__tests__/reader.jest.test.ts b/src/rulesets/__tests__/reader.jest.test.ts
index d69c24ac..13555a6c 100644
--- a/src/rulesets/__tests__/reader.jest.test.ts
+++ b/src/rulesets/__tests__/reader.jest.test.ts
@@ -17,10 +17,10 @@ const github447 = path.join(__dirname, './__fixtures__/github-issue-447-fixture.
 const enabledAllRuleset = path.join(__dirname, './__fixtures__/enable-all-ruleset.json');
 const invalidRuleset = path.join(__dirname, './__fixtures__/invalid-ruleset.json');
 const extendsOnlyRuleset = path.join(__dirname, './__fixtures__/extends-only-ruleset.json');
-const extendsAllOas2Ruleset = path.join(__dirname, './__fixtures__/extends-oas2-ruleset.json');
-const extendsUnspecifiedOas2Ruleset = path.join(__dirname, './__fixtures__/extends-unspecified-oas2-ruleset.json');
-const extendsDisabledOas2Ruleset = path.join(__dirname, './__fixtures__/extends-disabled-oas2-ruleset.yaml');
-const extendsOas2WithOverrideRuleset = path.join(__dirname, './__fixtures__/extends-oas2-with-override-ruleset.json');
+const extendsAllOasRuleset = path.join(__dirname, './__fixtures__/extends-oas-ruleset.json');
+const extendsUnspecifiedOasRuleset = path.join(__dirname, './__fixtures__/extends-unspecified-oas-ruleset.json');
+const extendsDisabledOasRuleset = path.join(__dirname, './__fixtures__/extends-disabled-oas-ruleset.yaml');
+const extendsOasWithOverrideRuleset = path.join(__dirname, './__fixtures__/extends-oas-with-override-ruleset.json');
 const extendsRelativeRuleset = path.join(__dirname, './__fixtures__/extends-relative-ruleset.json');
 const myOpenAPIRuleset = path.join(__dirname, './__fixtures__/my-open-api-ruleset.json');
 const fooRuleset = path.join(__dirname, './__fixtures__/foo-ruleset.json');
@@ -33,12 +33,7 @@ const fooCJSFunction = fs.readFileSync(path.join(__dirname, './__fixtures__/func
 const barFunction = fs.readFileSync(path.join(__dirname, './__fixtures__/customFunctions/bar.js'), 'utf8');
 const truthyFunction = fs.readFileSync(path.join(__dirname, './__fixtures__/customFunctions/truthy.js'), 'utf8');
 const oasRuleset = require('../oas/index.json');
-const oas2Ruleset = require('../oas2/index.json');
-const oas3Ruleset = require('../oas3/index.json');
-
 const oasRulesetRules: Dictionary<IRule, string> = oasRuleset.rules;
-const oas2RulesetRules: Dictionary<IRule, string> = oas2Ruleset.rules;
-const oas3RulesetRules: Dictionary<IRule, string> = oas3Ruleset.rules;
 
 jest.setTimeout(10000);
 
@@ -137,24 +132,21 @@ describe('Rulesets reader', () => {
   });
 
   it('should inherit properties of extended rulesets', async () => {
-    const { rules } = await readRuleset(extendsAllOas2Ruleset);
+    const { rules } = await readRuleset(extendsAllOasRuleset);
 
     // we pick up *all* rules only from spectral:oas and spectral:oas2 and keep their severity level or set a default one
     expect(rules).toEqual(
       expect.objectContaining({
-        ...[...Object.entries(oasRulesetRules), ...Object.entries(oas2RulesetRules)].reduce<Dictionary<IRule, string>>(
-          (oasRules, [name, rule]) => {
-            oasRules[name] = {
-              ...rule,
-              formats: expect.arrayContaining([expect.any(String)]),
-              ...((rule as IRule).severity === void 0 && { severity: DiagnosticSeverity.Warning }),
-              then: expect.any(Object),
-            };
+        ...Object.entries(oasRulesetRules).reduce<Dictionary<IRule, string>>((oasRules, [name, rule]) => {
+          oasRules[name] = {
+            ...rule,
+            formats: expect.arrayContaining([expect.any(String)]),
+            ...((rule as IRule).severity === void 0 && { severity: DiagnosticSeverity.Warning }),
+            then: expect.any(Object),
+          };
 
-            return oasRules;
-          },
-          {},
-        ),
+          return oasRules;
+        }, {}),
 
         'valid-rule': {
           given: '$.info',
@@ -167,12 +159,10 @@ describe('Rulesets reader', () => {
   });
 
   it('should inherit properties of extended rulesets and disable not recommended ones', () => {
-    return expect(readRuleset(extendsUnspecifiedOas2Ruleset)).resolves.toEqual(
+    return expect(readRuleset(extendsUnspecifiedOasRuleset)).resolves.toEqual(
       expect.objectContaining({
         rules: expect.objectContaining({
-          ...[...Object.entries(oasRulesetRules), ...Object.entries(oas3RulesetRules)].reduce<
-            Dictionary<IRule, string>
-          >((rules, [name, rule]) => {
+          ...Object.entries(oasRulesetRules).reduce<Dictionary<IRule, string>>((rules, [name, rule]) => {
             rules[name] = {
               ...rule,
               formats: expect.arrayContaining([expect.any(String)]),
@@ -210,7 +200,7 @@ describe('Rulesets reader', () => {
 
     expect(readRules).toEqual(
       expect.objectContaining({
-        ...[...Object.entries(oasRulesetRules), ...Object.entries(oas3RulesetRules)].reduce<Dictionary<IRule, string>>(
+        ...Object.entries(oasRulesetRules).reduce<Dictionary<IRule, string>>(
           (rules, [name, rule]) => {
             const formattedRule: IRule = {
               ...rule,
@@ -270,20 +260,17 @@ describe('Rulesets reader', () => {
   });
 
   it('should set severity of disabled rules to off', () => {
-    return expect(readRuleset(extendsDisabledOas2Ruleset)).resolves.toHaveProperty(
+    return expect(readRuleset(extendsDisabledOasRuleset)).resolves.toHaveProperty(
       'rules',
       expect.objectContaining({
-        ...[...Object.entries(oasRuleset.rules), ...Object.entries(oas2Ruleset.rules)].reduce<Dictionary<unknown>>(
-          (rules, [name, rule]) => {
-            rules[name] = expect.objectContaining({
-              description: (rule as IRule).description,
-              severity: -1,
-            });
+        ...Object.entries(oasRuleset.rules).reduce<Dictionary<unknown>>((rules, [name, rule]) => {
+          rules[name] = expect.objectContaining({
+            description: (rule as IRule).description,
+            severity: -1,
+          });
 
-            return rules;
-          },
-          {},
-        ),
+          return rules;
+        }, {}),
 
         'operation-operationId-unique': expect.objectContaining({
           // value of oasRuleset.rules['operation-operationId-unique']
@@ -299,7 +286,7 @@ describe('Rulesets reader', () => {
   });
 
   it('should override properties of extended rulesets', () => {
-    return expect(readRuleset(extendsOas2WithOverrideRuleset)).resolves.toHaveProperty('rules.operation-2xx-response', {
+    return expect(readRuleset(extendsOasWithOverrideRuleset)).resolves.toHaveProperty('rules.operation-2xx-response', {
       description: 'should be overridden',
       given: '$.info',
       formats: expect.arrayContaining([expect.any(String)]),
@@ -312,7 +299,7 @@ describe('Rulesets reader', () => {
   });
 
   it('should persist disabled properties of extended rulesets', () => {
-    return expect(readRuleset(extendsOas2WithOverrideRuleset)).resolves.toHaveProperty(
+    return expect(readRuleset(extendsOasWithOverrideRuleset)).resolves.toHaveProperty(
       'rules.oas2-operation-security-defined',
       {
         given: '$',
@@ -331,17 +318,14 @@ describe('Rulesets reader', () => {
     const { rules: enabledRules } = await readRuleset(enabledAllRuleset);
     expect(enabledRules).toEqual(
       expect.objectContaining(
-        [...Object.entries(oasRuleset.rules), ...Object.entries(oas2Ruleset.rules)].reduce<Dictionary<unknown>>(
-          (rules, [name, rule]) => {
-            rules[name] = expect.objectContaining({
-              description: (rule as IRule).description,
-              ...((rule as IRule).severity === undefined && { severity: DiagnosticSeverity.Warning }),
-            });
-
-            return rules;
-          },
-          {},
-        ),
+        Object.entries(oasRuleset.rules).reduce<Dictionary<unknown>>((rules, [name, rule]) => {
+          rules[name] = expect.objectContaining({
+            description: (rule as IRule).description,
+            ...((rule as IRule).severity === undefined && { severity: DiagnosticSeverity.Warning }),
+          });
+
+          return rules;
+        }, {}),
       ),
     );
 
@@ -353,40 +337,16 @@ describe('Rulesets reader', () => {
     ).toHaveLength(0);
   });
 
-  it('should limit the scope of formats to a ruleset', () => {
-    return expect(readRuleset(myOpenAPIRuleset)).resolves.toEqual(
-      expect.objectContaining({
-        rules: {
-          ...Object.entries(oasRuleset.rules).reduce<Dictionary<unknown>>((rules, [name, rule]) => {
-            rules[name] = expect.objectContaining({
-              formats: ['oas2', 'oas3'],
-            });
-
-            return rules;
-          }, {}),
-
-          ...Object.entries(oas2Ruleset.rules).reduce<Dictionary<unknown>>((rules, [name, rule]) => {
-            rules[name] = expect.objectContaining({
-              formats: ['oas2'],
-            });
-
-            return rules;
-          }, {}),
+  it('should limit the scope of formats to a ruleset', async () => {
+    const rules = (await readRuleset(myOpenAPIRuleset)).rules;
 
-          ...Object.entries(oas3Ruleset.rules).reduce<Dictionary<unknown>>((rules, [name, rule]) => {
-            rules[name] = expect.objectContaining({
-              formats: ['oas3'],
-            });
+    expect(Object.keys(rules)).toHaveLength(4);
 
-            return rules;
-          }, {}),
+    expect(rules['my-valid-rule'].formats).toBeUndefined();
 
-          'valid-rule': expect.objectContaining({
-            message: 'should be OK',
-          }),
-        },
-      }),
-    );
+    expect(rules['generic-valid-rule'].formats).toEqual(['oas2', 'oas3']);
+    expect(rules['oas2-valid-rule'].formats).toEqual(['oas2']);
+    expect(rules['oas3-valid-rule'].formats).toEqual(['oas3']);
   });
 
   it('given spectral:oas ruleset, should not pick up unrecommended rules', () => {
diff --git a/src/rulesets/__tests__/reader.test.ts b/src/rulesets/__tests__/reader.test.ts
index 65729121..b1c23b8d 100644
--- a/src/rulesets/__tests__/reader.test.ts
+++ b/src/rulesets/__tests__/reader.test.ts
@@ -2,7 +2,7 @@ import { readRuleset } from '../reader';
 
 describe('Rulesets reader', () => {
   it('should resolve oas2-schema', async () => {
-    const { rules } = await readRuleset('spectral:oas2');
+    const { rules } = await readRuleset('spectral:oas');
     expect(rules['oas2-schema']).not.toHaveProperty('then.functionOptions.schema.$ref');
     expect(rules['oas2-schema']).toHaveProperty(
       'then.functionOptions.schema',
@@ -15,7 +15,7 @@ describe('Rulesets reader', () => {
   });
 
   it('should resolve oas3-schema', async () => {
-    const { rules } = await readRuleset('spectral:oas3');
+    const { rules } = await readRuleset('spectral:oas');
     expect(rules['oas3-schema']).not.toHaveProperty('then.functionOptions.schema.$ref');
     expect(rules['oas3-schema']).toHaveProperty(
       'then.functionOptions.schema',
diff --git a/src/rulesets/__tests__/rule-name-conflicts.jest.test.ts b/src/rulesets/__tests__/rule-name-conflicts.jest.test.ts
deleted file mode 100644
index 4ff4d54b..00000000
--- a/src/rulesets/__tests__/rule-name-conflicts.jest.test.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import * as fs from 'fs';
-
-describe('Rule name conflicts among rulesets', () => {
-  test.each([
-    ['oas', 'oas2'],
-    ['oas2', 'oas3'],
-    ['oas3', 'oas'],
-  ])('do not exist (comparing %s with %s)', (one, another) => {
-    const oneContent = JSON.parse(fs.readFileSync(`${__dirname}/../${one}/index.json`, { encoding: 'utf8' }));
-    const anotherContent = JSON.parse(fs.readFileSync(`${__dirname}/../${another}/index.json`, { encoding: 'utf8' }));
-
-    interface IDescriptor {
-      name: string;
-      source: string;
-    }
-
-    const consolidated: Record<string, IDescriptor> = {};
-
-    const consolidate = (content: string, source: string) => {
-      Object.keys(content).forEach(key => {
-        consolidated[`${key}-${source}`] = { name: key, source };
-      });
-    };
-
-    consolidate(oneContent.rules, one);
-    consolidate(anotherContent.rules, another);
-
-    const sorted: Record<string, IDescriptor> = Object.keys(consolidated)
-      .sort()
-      .reduce(
-        (acc, key) => ({
-          ...acc,
-          [key]: consolidated[key],
-        }),
-        {},
-      );
-
-    const keys = Object.keys(sorted);
-    for (let i = 0; i < keys.length - 1; i++) {
-      const current = sorted[keys[i]];
-      const next = sorted[keys[i + 1]];
-      if (current.name !== next.name) {
-        continue;
-      }
-
-      throw new Error(`Rule name collision detected
-- ${current.name} (${current.source})
-- ${next.name} (${next.source})`);
-    }
-  });
-});
diff --git a/src/rulesets/oas/__tests__/example-value-or-externalValue.ts b/src/rulesets/oas/__tests__/example-value-or-externalValue.ts
index ec5a0333..e5381170 100644
--- a/src/rulesets/oas/__tests__/example-value-or-externalValue.ts
+++ b/src/rulesets/oas/__tests__/example-value-or-externalValue.ts
@@ -21,7 +21,7 @@ describe('example-value-or-externalValue', () => {
     expect(results.length).toEqual(0);
   });
 
-  test('return errors if missing externalValue and value', async () => {
+  test('return warnings if missing externalValue and value', async () => {
     const results = await s.run({ example: {} });
     expect(results).toEqual([
       {
@@ -43,7 +43,7 @@ describe('example-value-or-externalValue', () => {
     ]);
   });
 
-  test('return errors if both externalValue and value', async () => {
+  test('return warnings if both externalValue and value', async () => {
     const results = await s.run({ example: { externalValue: 'externalValue', value: 'value' } });
     expect(results).toEqual([
       {
@@ -60,7 +60,7 @@ describe('example-value-or-externalValue', () => {
             line: 1,
           },
         },
-        severity: 1,
+        severity: DiagnosticSeverity.Warning,
       },
     ]);
   });
diff --git a/src/rulesets/oas2/__tests__/oas2-anyOf.ts b/src/rulesets/oas/__tests__/oas2-anyOf.ts
similarity index 97%
rename from src/rulesets/oas2/__tests__/oas2-anyOf.ts
rename to src/rulesets/oas/__tests__/oas2-anyOf.ts
index ec3e5582..d79ee4c6 100644
--- a/src/rulesets/oas2/__tests__/oas2-anyOf.ts
+++ b/src/rulesets/oas/__tests__/oas2-anyOf.ts
@@ -3,6 +3,7 @@ import * as ruleset from '../index.json';
 
 describe('oas2-anyOf', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setRules({
     'oas2-anyOf': Object.assign(ruleset.rules['oas2-anyOf'], {
       recommended: true,
diff --git a/src/rulesets/oas2/__tests__/api-host.ts b/src/rulesets/oas/__tests__/oas2-api-host.ts
similarity index 80%
rename from src/rulesets/oas2/__tests__/api-host.ts
rename to src/rulesets/oas/__tests__/oas2-api-host.ts
index f515e3d6..90e3490d 100644
--- a/src/rulesets/oas2/__tests__/api-host.ts
+++ b/src/rulesets/oas/__tests__/oas2-api-host.ts
@@ -2,12 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('api-host', () => {
+describe('oas2-api-host', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setRules({
-    'api-host': Object.assign(ruleset.rules['api-host'], {
+    'oas2-api-host': Object.assign(ruleset.rules['oas2-api-host'], {
       recommended: true,
-      type: RuleType[ruleset.rules['api-host'].type],
+      type: RuleType[ruleset.rules['oas2-api-host'].type],
     }),
   });
 
@@ -28,7 +29,7 @@ describe('api-host', () => {
 
     expect(results).toEqual([
       {
-        code: 'api-host',
+        code: 'oas2-api-host',
         message: 'OpenAPI `host` must be present and non-empty string.',
         path: [],
         range: {
diff --git a/src/rulesets/oas2/__tests__/api-schemes.ts b/src/rulesets/oas/__tests__/oas2-api-schemes.ts
similarity index 84%
rename from src/rulesets/oas2/__tests__/api-schemes.ts
rename to src/rulesets/oas/__tests__/oas2-api-schemes.ts
index 9435c668..79b735ae 100644
--- a/src/rulesets/oas2/__tests__/api-schemes.ts
+++ b/src/rulesets/oas/__tests__/oas2-api-schemes.ts
@@ -2,12 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('api-schemes', () => {
+describe('oas2-api-schemes', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setRules({
-    'api-schemes': Object.assign(ruleset.rules['api-schemes'], {
+    'oas2-api-schemes': Object.assign(ruleset.rules['oas2-api-schemes'], {
       recommended: true,
-      type: RuleType[ruleset.rules['api-schemes'].type],
+      type: RuleType[ruleset.rules['oas2-api-schemes'].type],
     }),
   });
 
@@ -27,7 +28,7 @@ describe('api-schemes', () => {
     });
     expect(results).toEqual([
       {
-        code: 'api-schemes',
+        code: 'oas2-api-schemes',
         message: 'OpenAPI host `schemes` must be present and non-empty array.',
         path: [],
         range: {
@@ -53,7 +54,7 @@ describe('api-schemes', () => {
     });
     expect(results).toEqual([
       {
-        code: 'api-schemes',
+        code: 'oas2-api-schemes',
         message: 'OpenAPI host `schemes` must be present and non-empty array.',
         path: ['schemes'],
         range: {
diff --git a/src/rulesets/oas2/__tests__/host-not-example.ts b/src/rulesets/oas/__tests__/oas2-host-not-example.ts
similarity index 66%
rename from src/rulesets/oas2/__tests__/host-not-example.ts
rename to src/rulesets/oas/__tests__/oas2-host-not-example.ts
index b9bcfcc1..e845ffb9 100644
--- a/src/rulesets/oas2/__tests__/host-not-example.ts
+++ b/src/rulesets/oas/__tests__/oas2-host-not-example.ts
@@ -1,12 +1,13 @@
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('host-not-example', () => {
+describe('oas2-host-not-example', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setRules({
-    'host-not-example': Object.assign(ruleset.rules['host-not-example'], {
+    'oas2-host-not-example': Object.assign(ruleset.rules['oas2-host-not-example'], {
       recommended: true,
-      type: RuleType[ruleset.rules['host-not-example'].type],
+      type: RuleType[ruleset.rules['oas2-host-not-example'].type],
     }),
   });
 
@@ -27,8 +28,8 @@ describe('host-not-example', () => {
     });
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'host-not-example',
-        message: 'Server URL should not point at `example.com`.',
+        code: 'oas2-host-not-example',
+        message: 'Host URL should not point at example.com.',
         path: ['host'],
       }),
     ]);
diff --git a/src/rulesets/oas2/__tests__/host-trailing-slash.ts b/src/rulesets/oas/__tests__/oas2-host-trailing-slash.ts
similarity index 77%
rename from src/rulesets/oas2/__tests__/host-trailing-slash.ts
rename to src/rulesets/oas/__tests__/oas2-host-trailing-slash.ts
index cffc548a..7e988d78 100644
--- a/src/rulesets/oas2/__tests__/host-trailing-slash.ts
+++ b/src/rulesets/oas/__tests__/oas2-host-trailing-slash.ts
@@ -2,12 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('host-trailing-slash', () => {
+describe('oas2-host-trailing-slash', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setRules({
-    'host-trailing-slash': Object.assign(ruleset.rules['host-trailing-slash'], {
+    'oas2-host-trailing-slash': Object.assign(ruleset.rules['oas2-host-trailing-slash'], {
       recommended: true,
-      type: RuleType[ruleset.rules['host-trailing-slash'].type],
+      type: RuleType[ruleset.rules['oas2-host-trailing-slash'].type],
     }),
   });
 
@@ -28,7 +29,7 @@ describe('host-trailing-slash', () => {
     });
     expect(results).toEqual([
       {
-        code: 'host-trailing-slash',
+        code: 'oas2-host-trailing-slash',
         message: 'Server URL should not have a trailing slash.',
         path: ['host'],
         range: {
diff --git a/src/rulesets/oas2/__tests__/oas2-oneOf.ts b/src/rulesets/oas/__tests__/oas2-oneOf.ts
similarity index 90%
rename from src/rulesets/oas2/__tests__/oas2-oneOf.ts
rename to src/rulesets/oas/__tests__/oas2-oneOf.ts
index c8e24ba9..cd9778a1 100644
--- a/src/rulesets/oas2/__tests__/oas2-oneOf.ts
+++ b/src/rulesets/oas/__tests__/oas2-oneOf.ts
@@ -3,6 +3,7 @@ import * as ruleset from '../index.json';
 
 describe('oas2-oneOf', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setRules({
     'oas2-oneOf': Object.assign(ruleset.rules['oas2-oneOf'], {
       recommended: true,
@@ -25,7 +26,7 @@ describe('oas2-oneOf', () => {
               200: {
                 description: 'A paged array of pets',
                 schema: {
-                  oneOf: [{ type: 'string' }, { type: null }],
+                  oneOf: [{ type: 'string' }, { type: 'null' }],
                 },
               },
             },
@@ -40,7 +41,7 @@ describe('oas2-oneOf', () => {
         path: ['paths', '/test', 'get', 'responses', '200', 'schema', 'oneOf'],
         range: {
           end: {
-            character: 30,
+            character: 32,
             line: 21,
           },
           start: {
diff --git a/src/rulesets/oas2/__tests__/oas2-parameter-description.ts b/src/rulesets/oas/__tests__/oas2-parameter-description.ts
similarity index 96%
rename from src/rulesets/oas2/__tests__/oas2-parameter-description.ts
rename to src/rulesets/oas/__tests__/oas2-parameter-description.ts
index 1187c2e4..f3745cc5 100644
--- a/src/rulesets/oas2/__tests__/oas2-parameter-description.ts
+++ b/src/rulesets/oas/__tests__/oas2-parameter-description.ts
@@ -5,7 +5,7 @@ import * as ruleset from '../index.json';
 
 describe('oas2-parameter-description', () => {
   const s = new Spectral();
-
+  s.registerFormat('oas2', () => true);
   s.setRules({
     'oas2-parameter-description': Object.assign(ruleset.rules['oas2-parameter-description'], {
       recommended: true,
diff --git a/src/rulesets/oas2/__tests__/oas2-schema.ts b/src/rulesets/oas/__tests__/oas2-schema.ts
similarity index 92%
rename from src/rulesets/oas2/__tests__/oas2-schema.ts
rename to src/rulesets/oas/__tests__/oas2-schema.ts
index 6b5c8dfe..5585cc77 100644
--- a/src/rulesets/oas2/__tests__/oas2-schema.ts
+++ b/src/rulesets/oas/__tests__/oas2-schema.ts
@@ -1,10 +1,10 @@
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
-import * as oas2Schema from '../schemas/main.json';
+import * as oas2Schema from '../schemas/schema.oas2.json';
 
 describe('oas2-schema', () => {
   const s = new Spectral();
-
+  s.registerFormat('oas2', () => true);
   s.setRules({
     'oas2-schema': Object.assign({}, ruleset.rules['oas2-schema'], {
       recommended: true,
diff --git a/src/rulesets/oas2/__tests__/unusedDefinition.jest.test.ts b/src/rulesets/oas/__tests__/oas2-unused-definition.jest.test.ts
similarity index 93%
rename from src/rulesets/oas2/__tests__/unusedDefinition.jest.test.ts
rename to src/rulesets/oas/__tests__/oas2-unused-definition.jest.test.ts
index 8901e311..0ec4e5d4 100644
--- a/src/rulesets/oas2/__tests__/unusedDefinition.jest.test.ts
+++ b/src/rulesets/oas/__tests__/oas2-unused-definition.jest.test.ts
@@ -3,7 +3,7 @@ import * as path from '@stoplight/path';
 import { unreferencedReusableObject } from '../../../functions/unreferencedReusableObject';
 import { IParsedResult, RuleType, Spectral } from '../../../index';
 import { httpAndFileResolver } from '../../../resolvers/http-and-file';
-import { rules as oas2Rules } from '../../oas2/index.json';
+import { rules } from '../index.json';
 
 import { DiagnosticSeverity } from '@stoplight/types';
 import * as nock from 'nock';
@@ -11,11 +11,12 @@ import { readParsable } from '../../../fs/reader';
 
 describe('unusedDefinition - Http and fs remote references', () => {
   const s = new Spectral({ resolver: httpAndFileResolver });
+  s.registerFormat('oas2', () => true);
   s.setFunctions({ unreferencedReusableObject });
   s.setRules({
-    'unused-definition': Object.assign(oas2Rules['unused-definition'], {
+    'oas2-unused-definition': Object.assign(rules['oas2-unused-definition'], {
       recommended: true,
-      type: RuleType[oas2Rules['unused-definition'].type],
+      type: RuleType[rules['oas2-unused-definition'].type],
     }),
   });
 
@@ -86,7 +87,7 @@ describe('unusedDefinition - Http and fs remote references', () => {
 
       expect(results).toEqual([
         {
-          code: 'unused-definition',
+          code: 'oas2-unused-definition',
           message: 'Potentially unused definition has been detected.',
           path: ['definitions', 'Unhooked'],
           range: {
@@ -145,7 +146,7 @@ describe('unusedDefinition - Http and fs remote references', () => {
 
       expect(results).toEqual([
         {
-          code: 'unused-definition',
+          code: 'oas2-unused-definition',
           message: 'Potentially unused definition has been detected.',
           path: ['definitions', 'Unhooked'],
           range: {
diff --git a/src/rulesets/oas2/__tests__/unusedDefinition.karma.test.ts b/src/rulesets/oas/__tests__/oas2-unused-definition.karma.test.ts
similarity index 90%
rename from src/rulesets/oas2/__tests__/unusedDefinition.karma.test.ts
rename to src/rulesets/oas/__tests__/oas2-unused-definition.karma.test.ts
index 5b2c8687..c56dc1c5 100644
--- a/src/rulesets/oas2/__tests__/unusedDefinition.karma.test.ts
+++ b/src/rulesets/oas/__tests__/oas2-unused-definition.karma.test.ts
@@ -4,17 +4,18 @@ import { FetchMockSandbox } from 'fetch-mock';
 import { unreferencedReusableObject } from '../../../functions/unreferencedReusableObject';
 import { RuleType, Spectral } from '../../../index';
 import { httpAndFileResolver } from '../../../resolvers/http-and-file';
-import { rules as oas2Rules } from '../../oas2/index.json';
+import { rules } from '../index.json';
 
 describe('unusedDefinition - Http remote references', () => {
   let fetchMock: FetchMockSandbox;
 
   const s = new Spectral({ resolver: httpAndFileResolver });
+  s.registerFormat('oas2', () => true);
   s.setFunctions({ unreferencedReusableObject });
   s.setRules({
-    'unused-definition': Object.assign(oas2Rules['unused-definition'], {
+    'oas2-unused-definition': Object.assign(rules['oas2-unused-definition'], {
       recommended: true,
-      type: RuleType[oas2Rules['unused-definition'].type],
+      type: RuleType[rules['oas2-unused-definition'].type],
     }),
   });
 
@@ -83,7 +84,7 @@ describe('unusedDefinition - Http remote references', () => {
 
     expect(results).toEqual([
       {
-        code: 'unused-definition',
+        code: 'oas2-unused-definition',
         message: 'Potentially unused definition has been detected.',
         path: ['definitions', 'Unhooked'],
         range: {
diff --git a/src/rulesets/oas2/__tests__/unusedDefinition.test.ts b/src/rulesets/oas/__tests__/oas2-unused-definition.test.ts
similarity index 89%
rename from src/rulesets/oas2/__tests__/unusedDefinition.test.ts
rename to src/rulesets/oas/__tests__/oas2-unused-definition.test.ts
index 9a122416..46c1c526 100644
--- a/src/rulesets/oas2/__tests__/unusedDefinition.test.ts
+++ b/src/rulesets/oas/__tests__/oas2-unused-definition.test.ts
@@ -3,15 +3,16 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { unreferencedReusableObject } from '../../../functions/unreferencedReusableObject';
 import { RuleType, Spectral } from '../../../index';
 import { httpAndFileResolver } from '../../../resolvers/http-and-file';
-import { rules as oas2Rules } from '../../oas2/index.json';
+import { rules } from '../index.json';
 
-describe('unusedDefinition - Local references', () => {
+describe('oas2-unused-definition - local references', () => {
   const s = new Spectral({ resolver: httpAndFileResolver });
+  s.registerFormat('oas2', () => true);
   s.setFunctions({ unreferencedReusableObject });
   s.setRules({
-    'unused-definition': Object.assign(oas2Rules['unused-definition'], {
+    'oas2-unused-definition': Object.assign(rules['oas2-unused-definition'], {
       recommended: true,
-      type: RuleType[oas2Rules['unused-definition'].type],
+      type: RuleType[rules['oas2-unused-definition'].type],
     }),
   });
 
@@ -46,7 +47,7 @@ describe('unusedDefinition - Local references', () => {
         path: ['x-hook', '$ref'],
       }),
       {
-        code: 'unused-definition',
+        code: 'oas2-unused-definition',
         message: 'Potentially unused definition has been detected.',
         path: ['definitions', 'NotHooked'],
         range: {
@@ -126,7 +127,7 @@ describe('unusedDefinition - Local references', () => {
 
     expect(results).toEqual([
       {
-        code: 'unused-definition',
+        code: 'oas2-unused-definition',
         message: 'Potentially unused definition has been detected.',
         path: ['definitions', 'BouhouhouIamUnused'],
         range: {
diff --git a/src/rulesets/oas2/__tests__/valid-example-in-definitions.ts b/src/rulesets/oas/__tests__/oas2-valid-definition-example.ts
similarity index 93%
rename from src/rulesets/oas2/__tests__/valid-example-in-definitions.ts
rename to src/rulesets/oas/__tests__/oas2-valid-definition-example.ts
index 53fe52fe..fbea933c 100644
--- a/src/rulesets/oas2/__tests__/valid-example-in-definitions.ts
+++ b/src/rulesets/oas/__tests__/oas2-valid-definition-example.ts
@@ -2,13 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('valid-example-in-definitions', () => {
+describe('oas2-valid-definition-example', () => {
   const s = new Spectral();
-
+  s.registerFormat('oas2', () => true);
   s.setRules({
-    'valid-example-in-definitions': Object.assign(ruleset.rules['valid-example-in-definitions'], {
+    'oas2-valid-definition-example': Object.assign(ruleset.rules['oas2-valid-definition-example'], {
       recommended: true,
-      type: RuleType[ruleset.rules['valid-example-in-definitions'].type],
+      type: RuleType[ruleset.rules['oas2-valid-definition-example'].type],
     }),
   });
 
@@ -39,7 +39,7 @@ describe('valid-example-in-definitions', () => {
     });
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-definitions',
+        code: 'oas2-valid-definition-example',
         message: '"xoxo.example" property type should be string',
         severity: DiagnosticSeverity.Error,
       }),
@@ -103,7 +103,7 @@ describe('valid-example-in-definitions', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-definitions',
+        code: 'oas2-valid-definition-example',
         message: '"xoxo.example" property should have required property \'url\'',
         severity: DiagnosticSeverity.Error,
       }),
@@ -180,7 +180,7 @@ describe('valid-example-in-definitions', () => {
     expect(results).toEqual([
       expect.objectContaining({
         severity: DiagnosticSeverity.Error,
-        code: 'valid-example-in-definitions',
+        code: 'oas2-valid-definition-example',
         message: '"halRoot.example" property type should be array',
         path: ['definitions', 'halRoot', 'example', '_links', 'self'],
       }),
@@ -228,7 +228,7 @@ describe('valid-example-in-definitions', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-definitions',
+        code: 'oas2-valid-definition-example',
         message: '"c.example" property type should be string',
         path: ['definitions', '0', 'xoxo', 'properties', 'a', 'properties', 'b', 'properties', 'c', 'example'],
         range: expect.any(Object),
diff --git a/src/rulesets/oas2/__tests__/valid-example-in-parameters.ts b/src/rulesets/oas/__tests__/oas2-valid-parameter-example.ts
similarity index 93%
rename from src/rulesets/oas2/__tests__/valid-example-in-parameters.ts
rename to src/rulesets/oas/__tests__/oas2-valid-parameter-example.ts
index 8d5c4a59..8302f5d2 100644
--- a/src/rulesets/oas2/__tests__/valid-example-in-parameters.ts
+++ b/src/rulesets/oas/__tests__/oas2-valid-parameter-example.ts
@@ -2,12 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('valid-example-in-parameters', () => {
+describe('oas2-valid-parameter-example', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setRules({
-    'valid-example-in-parameters': Object.assign(ruleset.rules['valid-example-in-parameters'], {
+    'oas2-valid-parameter-example': Object.assign(ruleset.rules['oas2-valid-parameter-example'], {
       recommended: true,
-      type: RuleType[ruleset.rules['valid-example-in-parameters'].type],
+      type: RuleType[ruleset.rules['oas2-valid-parameter-example'].type],
     }),
   });
 
@@ -55,7 +56,7 @@ describe('valid-example-in-parameters', () => {
     });
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-parameters',
+        code: 'oas2-valid-parameter-example',
         message: '"schema.example" property type should be string',
         severity: DiagnosticSeverity.Error,
       }),
@@ -125,7 +126,7 @@ describe('valid-example-in-parameters', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-parameters',
+        code: 'oas2-valid-parameter-example',
         message: '"schema.example" property should have required property \'url\'',
         severity: DiagnosticSeverity.Error,
       }),
@@ -192,7 +193,7 @@ describe('valid-example-in-parameters', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-parameters',
+        code: 'oas2-valid-parameter-example',
         message: '"c.example" property type should be string',
         path: [
           'paths',
diff --git a/src/rulesets/oas3/__tests__/api-servers.ts b/src/rulesets/oas/__tests__/oas3-api-servers.ts
similarity index 84%
rename from src/rulesets/oas3/__tests__/api-servers.ts
rename to src/rulesets/oas/__tests__/oas3-api-servers.ts
index a2971518..9f183d54 100644
--- a/src/rulesets/oas3/__tests__/api-servers.ts
+++ b/src/rulesets/oas/__tests__/oas3-api-servers.ts
@@ -2,12 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('api-servers', () => {
+describe('oas3-api-servers', () => {
   const s = new Spectral();
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    'api-servers': Object.assign(ruleset.rules['api-servers'], {
+    'oas3-api-servers': Object.assign(ruleset.rules['oas3-api-servers'], {
       recommended: true,
-      type: RuleType[ruleset.rules['api-servers'].type],
+      type: RuleType[ruleset.rules['oas3-api-servers'].type],
     }),
   });
 
@@ -27,7 +28,7 @@ describe('api-servers', () => {
     });
     expect(results).toEqual([
       {
-        code: 'api-servers',
+        code: 'oas3-api-servers',
         message: 'OpenAPI `servers` must be present and non-empty array.',
         path: [],
         range: {
@@ -53,7 +54,7 @@ describe('api-servers', () => {
     });
     expect(results).toEqual([
       {
-        code: 'api-servers',
+        code: 'oas3-api-servers',
         message: 'OpenAPI `servers` must be present and non-empty array.',
         path: ['servers'],
         range: {
diff --git a/src/rulesets/oas3/__tests__/oas3-parameter-description.ts b/src/rulesets/oas/__tests__/oas3-parameter-description.ts
similarity index 98%
rename from src/rulesets/oas3/__tests__/oas3-parameter-description.ts
rename to src/rulesets/oas/__tests__/oas3-parameter-description.ts
index 908b622b..0c76f7e1 100644
--- a/src/rulesets/oas3/__tests__/oas3-parameter-description.ts
+++ b/src/rulesets/oas/__tests__/oas3-parameter-description.ts
@@ -5,7 +5,7 @@ import * as ruleset from '../index.json';
 
 describe('oas3-parameter-description', () => {
   const s = new Spectral();
-
+  s.registerFormat('oas3', () => true);
   s.setRules({
     'oas3-parameter-description': Object.assign(ruleset.rules['oas3-parameter-description'], {
       recommended: true,
diff --git a/src/rulesets/oas3/__tests__/server-not-example.ts b/src/rulesets/oas/__tests__/oas3-server-not-example.ts
similarity index 68%
rename from src/rulesets/oas3/__tests__/server-not-example.ts
rename to src/rulesets/oas/__tests__/oas3-server-not-example.ts
index 6a227d40..740b2ebc 100644
--- a/src/rulesets/oas3/__tests__/server-not-example.ts
+++ b/src/rulesets/oas/__tests__/oas3-server-not-example.ts
@@ -1,12 +1,13 @@
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('server-not-example.com', () => {
+describe('oas3-server-not-example.com', () => {
   const s = new Spectral();
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    'server-not-example.com': Object.assign(ruleset.rules['server-not-example.com'], {
+    'oas3-server-not-example.com': Object.assign(ruleset.rules['oas3-server-not-example.com'], {
       recommended: true,
-      type: RuleType[ruleset.rules['server-not-example.com'].type],
+      type: RuleType[ruleset.rules['oas3-server-not-example.com'].type],
     }),
   });
 
@@ -35,8 +36,8 @@ describe('server-not-example.com', () => {
     });
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'server-not-example.com',
-        message: 'Server URL should not point at `example.com`.',
+        code: 'oas3-server-not-example.com',
+        message: 'Server URL should not point at example.com.',
         path: ['servers', '0', 'url'],
       }),
     ]);
diff --git a/src/rulesets/oas3/__tests__/server-trailing-slash.ts b/src/rulesets/oas/__tests__/oas3-server-trailing-slash.ts
similarity index 79%
rename from src/rulesets/oas3/__tests__/server-trailing-slash.ts
rename to src/rulesets/oas/__tests__/oas3-server-trailing-slash.ts
index 0ce93044..bfc2d00f 100644
--- a/src/rulesets/oas3/__tests__/server-trailing-slash.ts
+++ b/src/rulesets/oas/__tests__/oas3-server-trailing-slash.ts
@@ -2,12 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('server-trailing-slash', () => {
+describe('oas3-server-trailing-slash', () => {
   const s = new Spectral();
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    'server-trailing-slash': Object.assign(ruleset.rules['server-trailing-slash'], {
+    'oas3-server-trailing-slash': Object.assign(ruleset.rules['oas3-server-trailing-slash'], {
       recommended: true,
-      type: RuleType[ruleset.rules['server-trailing-slash'].type],
+      type: RuleType[ruleset.rules['oas3-server-trailing-slash'].type],
     }),
   });
 
@@ -36,7 +37,7 @@ describe('server-trailing-slash', () => {
     });
     expect(results).toEqual([
       {
-        code: 'server-trailing-slash',
+        code: 'oas3-server-trailing-slash',
         message: 'Server URL should not have a trailing slash.',
         path: ['servers', '0', 'url'],
         range: {
diff --git a/src/rulesets/oas3/__tests__/unusedComponentsSchema.jest.test.ts b/src/rulesets/oas/__tests__/oas3-unused-components-schema.jest.test.ts
similarity index 93%
rename from src/rulesets/oas3/__tests__/unusedComponentsSchema.jest.test.ts
rename to src/rulesets/oas/__tests__/oas3-unused-components-schema.jest.test.ts
index 95ac600c..406d7c2f 100644
--- a/src/rulesets/oas3/__tests__/unusedComponentsSchema.jest.test.ts
+++ b/src/rulesets/oas/__tests__/oas3-unused-components-schema.jest.test.ts
@@ -1,21 +1,22 @@
 import { getLocationForJsonPath, parseWithPointers } from '@stoplight/json';
 import * as path from '@stoplight/path';
-import { unreferencedReusableObject } from '../../../functions/unreferencedReusableObject';
-import { IParsedResult, RuleType, Spectral } from '../../../index';
-import { httpAndFileResolver } from '../../../resolvers/http-and-file';
-import { rules as oas3Rules } from '../../oas3/index.json';
-
 import { DiagnosticSeverity } from '@stoplight/types';
 import * as nock from 'nock';
+
 import { readParsable } from '../../../fs/reader';
+import { unreferencedReusableObject } from '../../../functions/unreferencedReusableObject';
+import { IParsedResult, RuleType, Spectral } from '../../../index';
+import { httpAndFileResolver } from '../../../resolvers/http-and-file';
+import { rules } from '../index.json';
 
 describe('unusedComponentsSchema - Http and fs remote references', () => {
   const s = new Spectral({ resolver: httpAndFileResolver });
+  s.registerFormat('oas3', () => true);
   s.setFunctions({ unreferencedReusableObject });
   s.setRules({
-    'unused-components-schema': Object.assign(oas3Rules['unused-components-schema'], {
+    'oas3-unused-components-schema': Object.assign(rules['oas3-unused-components-schema'], {
       recommended: true,
-      type: RuleType[oas3Rules['unused-components-schema'].type],
+      type: RuleType[rules['oas3-unused-components-schema'].type],
     }),
   });
 
@@ -90,7 +91,7 @@ describe('unusedComponentsSchema - Http and fs remote references', () => {
 
       expect(results).toEqual([
         {
-          code: 'unused-components-schema',
+          code: 'oas3-unused-components-schema',
           message: 'Potentially unused components schema has been detected.',
           path: ['components', 'schemas', 'Unhooked'],
           range: {
@@ -149,7 +150,7 @@ describe('unusedComponentsSchema - Http and fs remote references', () => {
 
       expect(results).toEqual([
         {
-          code: 'unused-components-schema',
+          code: 'oas3-unused-components-schema',
           message: 'Potentially unused components schema has been detected.',
           path: ['components', 'schemas', 'Unhooked'],
           range: {
diff --git a/src/rulesets/oas3/__tests__/unusedComponentsSchema.karma.test.ts b/src/rulesets/oas/__tests__/oas3-unused-components-schema.karma.test.ts
similarity index 89%
rename from src/rulesets/oas3/__tests__/unusedComponentsSchema.karma.test.ts
rename to src/rulesets/oas/__tests__/oas3-unused-components-schema.karma.test.ts
index 76062376..ec005dc9 100644
--- a/src/rulesets/oas3/__tests__/unusedComponentsSchema.karma.test.ts
+++ b/src/rulesets/oas/__tests__/oas3-unused-components-schema.karma.test.ts
@@ -4,17 +4,18 @@ import { FetchMockSandbox } from 'fetch-mock';
 import { unreferencedReusableObject } from '../../../functions/unreferencedReusableObject';
 import { RuleType, Spectral } from '../../../index';
 import { httpAndFileResolver } from '../../../resolvers/http-and-file';
-import { rules as oas3Rules } from '../../oas3/index.json';
+import { rules } from '../index.json';
 
 describe('unusedComponentsSchema - Http remote references', () => {
   let fetchMock: FetchMockSandbox;
 
   const s = new Spectral({ resolver: httpAndFileResolver });
+  s.registerFormat('oas3', () => true);
   s.setFunctions({ unreferencedReusableObject });
   s.setRules({
-    'unused-components-schema': Object.assign(oas3Rules['unused-components-schema'], {
+    'oas3-unused-components-schema': Object.assign(rules['oas3-unused-components-schema'], {
       recommended: true,
-      type: RuleType[oas3Rules['unused-components-schema'].type],
+      type: RuleType[rules['oas3-unused-components-schema'].type],
     }),
   });
 
@@ -87,7 +88,7 @@ describe('unusedComponentsSchema - Http remote references', () => {
 
     expect(results).toEqual([
       {
-        code: 'unused-components-schema',
+        code: 'oas3-unused-components-schema',
         message: 'Potentially unused components schema has been detected.',
         path: ['components', 'schemas', 'Unhooked'],
         range: {
diff --git a/src/rulesets/oas3/__tests__/unusedComponentsSchema.test.ts b/src/rulesets/oas/__tests__/oas3-unused-components-schema.test.ts
similarity index 91%
rename from src/rulesets/oas3/__tests__/unusedComponentsSchema.test.ts
rename to src/rulesets/oas/__tests__/oas3-unused-components-schema.test.ts
index 055bf40b..c3fb7b90 100644
--- a/src/rulesets/oas3/__tests__/unusedComponentsSchema.test.ts
+++ b/src/rulesets/oas/__tests__/oas3-unused-components-schema.test.ts
@@ -3,15 +3,16 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { unreferencedReusableObject } from '../../../functions/unreferencedReusableObject';
 import { RuleType, Spectral } from '../../../index';
 import { httpAndFileResolver } from '../../../resolvers/http-and-file';
-import { rules as oas3Rules } from '../../oas3/index.json';
+import { rules } from '../index.json';
 
 describe('unusedComponentsSchema - Local references', () => {
   const s = new Spectral({ resolver: httpAndFileResolver });
+  s.registerFormat('oas3', () => true);
   s.setFunctions({ unreferencedReusableObject });
   s.setRules({
-    'unused-components-schema': Object.assign(oas3Rules['unused-components-schema'], {
+    'oas3-unused-components-schema': Object.assign(rules['oas3-unused-components-schema'], {
       recommended: true,
-      type: RuleType[oas3Rules['unused-components-schema'].type],
+      type: RuleType[rules['oas3-unused-components-schema'].type],
     }),
   });
 
@@ -48,7 +49,7 @@ describe('unusedComponentsSchema - Local references', () => {
         path: ['x-hook', '$ref'],
       }),
       {
-        code: 'unused-components-schema',
+        code: 'oas3-unused-components-schema',
         message: 'Potentially unused components schema has been detected.',
         path: ['components', 'schemas', 'NotHooked'],
         range: {
@@ -132,7 +133,7 @@ describe('unusedComponentsSchema - Local references', () => {
 
     expect(results).toEqual([
       {
-        code: 'unused-components-schema',
+        code: 'oas3-unused-components-schema',
         message: 'Potentially unused components schema has been detected.',
         path: ['components', 'schemas', 'BouhouhouIamUnused'],
         range: {
diff --git a/src/rulesets/oas3/__tests__/valid-schema-example-in-content.ts b/src/rulesets/oas/__tests__/oas3-valid-content-schema-example.ts
similarity index 67%
rename from src/rulesets/oas3/__tests__/valid-schema-example-in-content.ts
rename to src/rulesets/oas/__tests__/oas3-valid-content-schema-example.ts
index 94189d58..28596f4f 100644
--- a/src/rulesets/oas3/__tests__/valid-schema-example-in-content.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-content-schema-example.ts
@@ -1,5 +1,5 @@
 import testRule from './templates/_schema-example';
 
-const ruleName = 'valid-schema-example-in-content';
+const ruleName = 'oas3-valid-content-schema-example';
 
 describe(ruleName, () => testRule(ruleName, 'content'));
diff --git a/src/rulesets/oas3/__tests__/valid-schema-example-in-headers.ts b/src/rulesets/oas/__tests__/oas3-valid-header-schema-example.ts
similarity index 67%
rename from src/rulesets/oas3/__tests__/valid-schema-example-in-headers.ts
rename to src/rulesets/oas/__tests__/oas3-valid-header-schema-example.ts
index 075cb9e1..674b0e3d 100644
--- a/src/rulesets/oas3/__tests__/valid-schema-example-in-headers.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-header-schema-example.ts
@@ -1,5 +1,5 @@
 import testRule from './templates/_schema-example';
 
-const ruleName = 'valid-schema-example-in-headers';
+const ruleName = 'oas3-valid-header-schema-example';
 
 describe(ruleName, () => testRule(ruleName, 'headers'));
diff --git a/src/rulesets/oas3/__tests__/valid-oas-example-in-content.ts b/src/rulesets/oas/__tests__/oas3-valid-oas-content-example.ts
similarity index 67%
rename from src/rulesets/oas3/__tests__/valid-oas-example-in-content.ts
rename to src/rulesets/oas/__tests__/oas3-valid-oas-content-example.ts
index a4a347fd..6929c941 100644
--- a/src/rulesets/oas3/__tests__/valid-oas-example-in-content.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-oas-content-example.ts
@@ -1,5 +1,5 @@
 import testRule from './templates/_oas-example';
 
-const ruleName = 'valid-oas-example-in-content';
+const ruleName = 'oas3-valid-oas-content-example';
 
 describe(ruleName, () => testRule(ruleName, 'content'));
diff --git a/src/rulesets/oas3/__tests__/valid-oas-example-in-headers.ts b/src/rulesets/oas/__tests__/oas3-valid-oas-header-example.ts
similarity index 68%
rename from src/rulesets/oas3/__tests__/valid-oas-example-in-headers.ts
rename to src/rulesets/oas/__tests__/oas3-valid-oas-header-example.ts
index b65f33cd..10569e87 100644
--- a/src/rulesets/oas3/__tests__/valid-oas-example-in-headers.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-oas-header-example.ts
@@ -1,5 +1,5 @@
 import testRule from './templates/_oas-example';
 
-const ruleName = 'valid-oas-example-in-headers';
+const ruleName = 'oas3-valid-oas-header-example';
 
 describe(ruleName, () => testRule(ruleName, 'headers'));
diff --git a/src/rulesets/oas3/__tests__/valid-oas-example-in-parameters.ts b/src/rulesets/oas/__tests__/oas3-valid-oas-parameter-example.ts
similarity index 89%
rename from src/rulesets/oas3/__tests__/valid-oas-example-in-parameters.ts
rename to src/rulesets/oas/__tests__/oas3-valid-oas-parameter-example.ts
index c6a4ec40..84064840 100644
--- a/src/rulesets/oas3/__tests__/valid-oas-example-in-parameters.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-oas-parameter-example.ts
@@ -2,13 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('valid-oas-example-in-parameters', () => {
+describe('oas3-valid-oas-parameter-example', () => {
   const s = new Spectral();
-
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    'valid-oas-example-in-parameters': Object.assign(ruleset.rules['valid-oas-example-in-parameters'], {
+    'oas3-valid-oas-parameter-example': Object.assign(ruleset.rules['oas3-valid-oas-parameter-example'], {
       recommended: true,
-      type: RuleType[ruleset.rules['valid-oas-example-in-parameters'].type],
+      type: RuleType[ruleset.rules['oas3-valid-oas-parameter-example'].type],
     }),
   });
 
@@ -40,7 +40,7 @@ describe('valid-oas-example-in-parameters', () => {
     expect(results).toEqual([
       expect.objectContaining({
         severity: DiagnosticSeverity.Error,
-        code: 'valid-oas-example-in-parameters',
+        code: 'oas3-valid-oas-parameter-example',
         message: '"0.example" property type should be string',
       }),
     ]);
@@ -118,7 +118,7 @@ describe('valid-oas-example-in-parameters', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-oas-example-in-parameters',
+        code: 'oas3-valid-oas-parameter-example',
         message: '"Abc.example" property should have required property \'abc\'',
         severity: DiagnosticSeverity.Error,
       }),
@@ -155,7 +155,7 @@ describe('valid-oas-example-in-parameters', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-oas-example-in-parameters',
+        code: 'oas3-valid-oas-parameter-example',
         message: '"0.example" property should have required property \'url\'',
         severity: DiagnosticSeverity.Error,
       }),
diff --git a/src/rulesets/oas3/__tests__/valid-schema-example-in-parameters.ts b/src/rulesets/oas/__tests__/oas3-valid-parameter-schema-example.ts
similarity index 93%
rename from src/rulesets/oas3/__tests__/valid-schema-example-in-parameters.ts
rename to src/rulesets/oas/__tests__/oas3-valid-parameter-schema-example.ts
index 625131cd..566e0619 100644
--- a/src/rulesets/oas3/__tests__/valid-schema-example-in-parameters.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-parameter-schema-example.ts
@@ -2,13 +2,13 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('valid-schema-example-in-parameters', () => {
+describe('oas3-valid-parameter-schema-example', () => {
   const s = new Spectral();
-
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    'valid-schema-example-in-parameters': Object.assign(ruleset.rules['valid-schema-example-in-parameters'], {
+    'oas3-valid-parameter-schema-example': Object.assign(ruleset.rules['oas3-valid-parameter-schema-example'], {
       recommended: true,
-      type: RuleType[ruleset.rules['valid-schema-example-in-parameters'].type],
+      type: RuleType[ruleset.rules['oas3-valid-parameter-schema-example'].type],
     }),
   });
 
@@ -86,7 +86,7 @@ describe('valid-schema-example-in-parameters', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-schema-example-in-parameters',
+        code: 'oas3-valid-parameter-schema-example',
         message: '"c.example" property type should be string',
         path: [
           'paths',
@@ -168,7 +168,7 @@ describe('valid-schema-example-in-parameters', () => {
     expect(results).toEqual([
       expect.objectContaining({
         severity: DiagnosticSeverity.Error,
-        code: 'valid-schema-example-in-parameters',
+        code: 'oas3-valid-parameter-schema-example',
         message: '"schema.example" property type should be string',
       }),
     ]);
@@ -246,7 +246,7 @@ describe('valid-schema-example-in-parameters', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-schema-example-in-parameters',
+        code: 'oas3-valid-parameter-schema-example',
         message: '"abc.example" property type should be number',
         severity: DiagnosticSeverity.Error,
       }),
@@ -283,7 +283,7 @@ describe('valid-schema-example-in-parameters', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-schema-example-in-parameters',
+        code: 'oas3-valid-parameter-schema-example',
         message: '"schema.example" property should have required property \'url\'',
         severity: DiagnosticSeverity.Error,
       }),
diff --git a/src/rulesets/oas3/__tests__/valid-example-in-schemas.ts b/src/rulesets/oas/__tests__/oas3-valid-schema-example.ts
similarity index 89%
rename from src/rulesets/oas3/__tests__/valid-example-in-schemas.ts
rename to src/rulesets/oas/__tests__/oas3-valid-schema-example.ts
index e9cb9bcc..8f9e9d6a 100644
--- a/src/rulesets/oas3/__tests__/valid-example-in-schemas.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-schema-example.ts
@@ -2,18 +2,19 @@ import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
-describe('valid-example-in-schemas', () => {
+describe('oas3-valid-schema-example', () => {
   const s = new Spectral();
-
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    'valid-example-in-schemas': Object.assign(ruleset.rules['valid-example-in-schemas'], {
+    'oas3-valid-schema-example': Object.assign(ruleset.rules['oas3-valid-schema-example'], {
       recommended: true,
-      type: RuleType[ruleset.rules['valid-example-in-schemas'].type],
+      type: RuleType[ruleset.rules['oas3-valid-schema-example']!.type],
     }),
   });
 
   test('will pass when simple example is valid', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       components: {
         schemas: {
           xoxo: {
@@ -28,6 +29,7 @@ describe('valid-example-in-schemas', () => {
 
   test('will fail when simple example is invalid', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       components: {
         schemas: {
           xoxo: {
@@ -40,7 +42,7 @@ describe('valid-example-in-schemas', () => {
     expect(results).toEqual([
       expect.objectContaining({
         severity: DiagnosticSeverity.Error,
-        code: 'valid-example-in-schemas',
+        code: 'oas3-valid-schema-example',
         message: '"xoxo.example" property type should be string',
       }),
     ]);
@@ -48,7 +50,7 @@ describe('valid-example-in-schemas', () => {
 
   test('will pass for valid parents examples which contain invalid child examples', async () => {
     const results = await s.run({
-      swagger: '2.0',
+      openapi: '3.0.2',
       info: {
         version: '1.0.0',
         title: 'Swagger Petstore',
@@ -94,7 +96,7 @@ describe('valid-example-in-schemas', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-schemas',
+        code: 'oas3-valid-schema-example',
         message: '"c.example" property type should be string',
         path: [
           'components',
@@ -117,6 +119,7 @@ describe('valid-example-in-schemas', () => {
 
   test('will pass when complex example is used ', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       components: {
         schemas: {
           xoxo: {
@@ -148,6 +151,7 @@ describe('valid-example-in-schemas', () => {
 
   test('will fail when complex example is used', async () => {
     const data = {
+      openapi: '3.0.2',
       components: {
         schemas: {
           xoxo: {
@@ -183,7 +187,7 @@ describe('valid-example-in-schemas', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-schemas',
+        code: 'oas3-valid-schema-example',
         message: '"abc.example" property type should be number',
         severity: DiagnosticSeverity.Error,
       }),
@@ -192,6 +196,7 @@ describe('valid-example-in-schemas', () => {
 
   test('will error with totally invalid input', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       components: {
         schemas: {
           xoxo: {
@@ -220,7 +225,7 @@ describe('valid-example-in-schemas', () => {
 
     expect(results).toEqual([
       expect.objectContaining({
-        code: 'valid-example-in-schemas',
+        code: 'oas3-valid-schema-example',
         message: '"xoxo.example" property should have required property \'url\'',
         severity: DiagnosticSeverity.Error,
       }),
diff --git a/src/rulesets/oas/__tests__/operation-summary-formatted.ts b/src/rulesets/oas/__tests__/operation-summary-formatted.ts
deleted file mode 100644
index 2422134b..00000000
--- a/src/rulesets/oas/__tests__/operation-summary-formatted.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { DiagnosticSeverity } from '@stoplight/types';
-import { RuleType, Spectral } from '../../../spectral';
-import * as ruleset from '../index.json';
-
-describe('operation-summary-formatted', () => {
-  const s = new Spectral();
-  s.setRules({
-    'operation-summary-formatted': Object.assign(ruleset.rules['operation-summary-formatted'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['operation-summary-formatted'].type],
-    }),
-  });
-
-  test('validate a correct object', async () => {
-    const results = await s.run({
-      swagger: '2.0',
-      paths: {
-        '/todos': {
-          get: {
-            summary: 'This is a valid summary.',
-          },
-        },
-      },
-    });
-    expect(results.length).toEqual(0);
-  });
-
-  test('return errors if summary does not start with an uppercase', async () => {
-    const results = await s.run({
-      swagger: '2.0',
-      paths: {
-        '/todos': {
-          get: {
-            summary: 'this is not a valid summary.',
-          },
-        },
-      },
-    });
-    expect(results).toEqual([
-      {
-        code: 'operation-summary-formatted',
-        message: 'Operation `summary` should start with upper case and end with a dot.',
-        path: ['paths', '/todos', 'get', 'summary'],
-        range: {
-          end: {
-            character: 49,
-            line: 5,
-          },
-          start: {
-            character: 19,
-            line: 5,
-          },
-        },
-        severity: DiagnosticSeverity.Warning,
-      },
-    ]);
-  });
-
-  test('return errors if summary does not end with a dot', async () => {
-    const results = await s.run({
-      swagger: '2.0',
-      paths: {
-        '/todos': {
-          get: {
-            summary: 'This is not a valid summary',
-          },
-        },
-      },
-    });
-    expect(results).toEqual([
-      {
-        code: 'operation-summary-formatted',
-        message: 'Operation `summary` should start with upper case and end with a dot.',
-        path: ['paths', '/todos', 'get', 'summary'],
-        range: {
-          end: {
-            character: 48,
-            line: 5,
-          },
-          start: {
-            character: 19,
-            line: 5,
-          },
-        },
-        severity: DiagnosticSeverity.Warning,
-      },
-    ]);
-  });
-});
diff --git a/src/rulesets/oas3/__tests__/templates/_oas-example.ts b/src/rulesets/oas/__tests__/templates/_oas-example.ts
similarity index 93%
rename from src/rulesets/oas3/__tests__/templates/_oas-example.ts
rename to src/rulesets/oas/__tests__/templates/_oas-example.ts
index 9be18040..81c14082 100644
--- a/src/rulesets/oas3/__tests__/templates/_oas-example.ts
+++ b/src/rulesets/oas/__tests__/templates/_oas-example.ts
@@ -1,19 +1,20 @@
 import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../../spectral';
-import * as ruleset from '../../index.json';
+import { rules } from '../../index.json';
 
 export default (ruleName: string, path: string) => {
   const s = new Spectral();
-
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    [ruleName]: Object.assign(ruleset.rules[ruleName], {
+    [ruleName]: Object.assign(rules[ruleName], {
       recommended: true,
-      type: RuleType[ruleset.rules[ruleName].type],
+      type: RuleType[rules[ruleName].type],
     }),
   });
 
   test('will pass when simple example is valid', async () => {
     const results = await s.run({
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
@@ -28,6 +29,7 @@ export default (ruleName: string, path: string) => {
 
   test('will fail when simple example is invalid', async () => {
     const results = await s.run({
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
@@ -48,6 +50,7 @@ export default (ruleName: string, path: string) => {
 
   test('will pass when complex example is used ', async () => {
     const results = await s.run({
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
@@ -79,6 +82,7 @@ export default (ruleName: string, path: string) => {
 
   test('will fail when complex example is used ', async () => {
     const data = {
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
@@ -119,6 +123,7 @@ export default (ruleName: string, path: string) => {
 
   test('will error with totally invalid input', async () => {
     const results = await s.run({
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
@@ -161,6 +166,7 @@ export default (ruleName: string, path: string) => {
 
   test('does not report example mismatches for unknown AJV formats', async () => {
     const results = await s.run({
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
@@ -182,6 +188,7 @@ export default (ruleName: string, path: string) => {
     ['float', 2 ** 128],
   ])('reports invalid usage of %s format', async (format, example) => {
     const results = await s.run({
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
@@ -215,6 +222,7 @@ export default (ruleName: string, path: string) => {
     ['double', 2 ** 1028],
   ])('does not report valid usage of %s format', async (format, example) => {
     const results = await s.run({
+      openapi: '3.0.0',
       [path]: {
         xoxo: {
           schema: {
diff --git a/src/rulesets/oas3/__tests__/templates/_schema-example.ts b/src/rulesets/oas/__tests__/templates/_schema-example.ts
similarity index 94%
rename from src/rulesets/oas3/__tests__/templates/_schema-example.ts
rename to src/rulesets/oas/__tests__/templates/_schema-example.ts
index d9e60fb4..7672bcc5 100644
--- a/src/rulesets/oas3/__tests__/templates/_schema-example.ts
+++ b/src/rulesets/oas/__tests__/templates/_schema-example.ts
@@ -1,19 +1,20 @@
 import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../../spectral';
-import * as ruleset from '../../index.json';
+import { rules } from '../../index.json';
 
 export default (ruleName: string, path: string) => {
   const s = new Spectral();
-
+  s.registerFormat('oas3', () => true);
   s.setRules({
-    [ruleName]: Object.assign(ruleset.rules[ruleName], {
+    [ruleName]: Object.assign(rules[ruleName], {
       recommended: true,
-      type: RuleType[ruleset.rules[ruleName].type],
+      type: RuleType[rules[ruleName].type],
     }),
   });
 
   test('will pass when simple example is valid', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: {
           schema: {
@@ -28,6 +29,7 @@ export default (ruleName: string, path: string) => {
 
   test('will fail when simple example is invalid', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: {
           schema: {
@@ -48,6 +50,7 @@ export default (ruleName: string, path: string) => {
 
   test('will pass for valid parents examples which contain invalid child examples', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: [
           {
@@ -100,6 +103,7 @@ export default (ruleName: string, path: string) => {
 
   test('will pass when complex example is used ', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: {
           schema: {
@@ -131,6 +135,7 @@ export default (ruleName: string, path: string) => {
 
   test('will fail when complex example is used ', async () => {
     const data = {
+      openapi: '3.0.2',
       [path]: {
         Heh: {
           schema: {
@@ -177,6 +182,7 @@ export default (ruleName: string, path: string) => {
 
   test('will error with totally invalid input', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: {
           schema: {
@@ -214,6 +220,7 @@ export default (ruleName: string, path: string) => {
 
   test('does not report example mismatches for unknown AJV formats', async () => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: {
           schema: {
@@ -240,6 +247,7 @@ export default (ruleName: string, path: string) => {
     ['float', 2 ** 128],
   ])('reports invalid usage of %s format', async (format, example) => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: {
           schema: {
@@ -273,6 +281,7 @@ export default (ruleName: string, path: string) => {
     ['double', 2 ** 1028],
   ])('does not report valid usage of %s format', async (format, example) => {
     const results = await s.run({
+      openapi: '3.0.2',
       [path]: {
         xoxo: {
           schema: {
diff --git a/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts b/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts
index 42336103..38da524b 100644
--- a/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts
@@ -5,16 +5,18 @@ import oasOpFormDataConsumeCheck from '../oasOpFormDataConsumeCheck';
 
 describe('oasOpFormDataConsumeCheck', () => {
   const s = new Spectral();
+  s.registerFormat('oas2', () => true);
   s.setFunctions({ oasOpFormDataConsumeCheck });
   s.setRules({
-    'operation-formData-consume-check': Object.assign(rules['operation-formData-consume-check'], {
+    'oas2-operation-formData-consume-check': Object.assign(rules['oas2-operation-formData-consume-check'], {
       recommended: true,
-      type: RuleType[rules['operation-formData-consume-check'].type],
+      type: RuleType[rules['oas2-operation-formData-consume-check'].type],
     }),
   });
 
   test('validate a correct object', async () => {
     const results = await s.run({
+      swagger: '2.0',
       paths: {
         '/path1': {
           get: {
@@ -29,6 +31,7 @@ describe('oasOpFormDataConsumeCheck', () => {
 
   test('return errors on different path operations same id', async () => {
     const results = await s.run({
+      swagger: '2.0',
       paths: {
         '/path1': {
           get: {
@@ -41,18 +44,18 @@ describe('oasOpFormDataConsumeCheck', () => {
 
     expect(results).toEqual([
       {
-        code: 'operation-formData-consume-check',
+        code: 'oas2-operation-formData-consume-check',
         message:
           'Operations with an `in: formData` parameter must include `application/x-www-form-urlencoded` or `multipart/form-data` in their `consumes` property.',
         path: ['paths', '/path1', 'get'],
         range: {
           end: {
             character: 26,
-            line: 10,
+            line: 11,
           },
           start: {
             character: 12,
-            line: 3,
+            line: 4,
           },
         },
         severity: DiagnosticSeverity.Warning,
diff --git a/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts b/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts
index 27a32d3e..d1071209 100644
--- a/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts
@@ -1,17 +1,17 @@
 import { DiagnosticSeverity } from '@stoplight/types';
 import { RuleType, Spectral } from '../../../../index';
-import { rules as oas2Rules } from '../../../oas2/index.json';
-import { rules as oas3Rules } from '../../../oas3/index.json';
+import { rules as oasRules } from '../../../oas/index.json';
 import oasOpSecurityDefined from '../oasOpSecurityDefined';
 
 describe('oasOpSecurityDefined', () => {
   describe('oas2', () => {
     const s = new Spectral();
+    s.registerFormat('oas2', () => true);
     s.setFunctions({ oasOpSecurityDefined });
     s.setRules({
-      'oas2-operation-security-defined': Object.assign(oas2Rules['oas2-operation-security-defined'], {
+      'oas2-operation-security-defined': Object.assign(oasRules['oas2-operation-security-defined'], {
         recommended: true,
-        type: RuleType[oas2Rules['oas2-operation-security-defined'].type],
+        type: RuleType[oasRules['oas2-operation-security-defined'].type],
       }),
     });
 
@@ -37,6 +37,7 @@ describe('oasOpSecurityDefined', () => {
 
     test('return errors on invalid object', async () => {
       const results = await s.run({
+        swagger: '2.0',
         securityDefinitions: {},
         paths: {
           '/path': {
@@ -52,38 +53,30 @@ describe('oasOpSecurityDefined', () => {
       });
 
       expect(results).toEqual([
-        {
+        expect.objectContaining({
           code: 'oas2-operation-security-defined',
           message: 'Operation `security` values must match a scheme defined in the `securityDefinitions` object.',
           path: ['paths', '/path', 'get', 'security', '0'],
-          range: {
-            end: {
-              character: 24,
-              line: 7,
-            },
-            start: {
-              character: 10,
-              line: 6,
-            },
-          },
           severity: DiagnosticSeverity.Warning,
-        },
+        }),
       ]);
     });
   });
 
   describe('oas3', () => {
     const s = new Spectral();
+    s.registerFormat('oas3', () => true);
     s.setFunctions({ oasOpSecurityDefined });
     s.setRules({
-      'oas3-operation-security-defined': Object.assign(oas3Rules['oas3-operation-security-defined'], {
+      'oas3-operation-security-defined': Object.assign(oasRules['oas3-operation-security-defined'], {
         recommended: true,
-        type: RuleType[oas3Rules['oas3-operation-security-defined'].type],
+        type: RuleType[oasRules['oas3-operation-security-defined'].type],
       }),
     });
 
     test('validate a correct object (just in body)', async () => {
       const results = await s.run({
+        openapi: '3.0.2',
         components: {
           securitySchemes: {
             apikey: {},
@@ -106,6 +99,7 @@ describe('oasOpSecurityDefined', () => {
 
     test('return errors on invalid object', async () => {
       const results = await s.run({
+        openapi: '3.0.2',
         components: {},
         paths: {
           '/path': {
@@ -121,23 +115,13 @@ describe('oasOpSecurityDefined', () => {
       });
 
       expect(results).toEqual([
-        {
+        expect.objectContaining({
           code: 'oas3-operation-security-defined',
           message:
             'Operation `security` values must match a scheme defined in the `components.securitySchemes` object.',
           path: ['paths', '/path', 'get', 'security', '0'],
-          range: {
-            end: {
-              character: 24,
-              line: 7,
-            },
-            start: {
-              character: 10,
-              line: 6,
-            },
-          },
           severity: DiagnosticSeverity.Warning,
-        },
+        }),
       ]);
     });
   });
diff --git a/src/rulesets/oas2/schemas/main.json b/src/rulesets/oas/schemas/schema.oas2.json
similarity index 100%
rename from src/rulesets/oas2/schemas/main.json
rename to src/rulesets/oas/schemas/schema.oas2.json
diff --git a/src/rulesets/oas3/schemas/main.json b/src/rulesets/oas/schemas/schema.oas3.json
similarity index 100%
rename from src/rulesets/oas3/schemas/main.json
rename to src/rulesets/oas/schemas/schema.oas3.json
diff --git a/src/rulesets/oas2/__tests__/model-description.ts b/src/rulesets/oas2/__tests__/model-description.ts
deleted file mode 100644
index 4156674b..00000000
--- a/src/rulesets/oas2/__tests__/model-description.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { DiagnosticSeverity } from '@stoplight/types';
-import { RuleType, Spectral } from '../../../spectral';
-import * as ruleset from '../index.json';
-
-describe('definition-description', () => {
-  const s = new Spectral();
-  s.setRules({
-    'definition-description': Object.assign(ruleset.rules['definition-description'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['definition-description'].type],
-    }),
-  });
-
-  test('validate a correct object', async () => {
-    const results = await s.run({
-      swagger: '2.0',
-      paths: {},
-      host: 'stoplight.io',
-      definitions: {
-        user: {
-          description: 'this describes the user model',
-        },
-      },
-    });
-    expect(results.length).toEqual(0);
-  });
-
-  test('return errors if a definition is missing description', async () => {
-    const results = await s.run({
-      swagger: '2.0',
-      paths: {},
-      host: 'stoplight.io',
-      definitions: { user: {} },
-    });
-    expect(results).toEqual([
-      {
-        code: 'definition-description',
-        message: 'Definition `description` must be present and non-empty string.',
-        path: ['definitions', 'user'],
-        range: {
-          end: {
-            character: 14,
-            line: 5,
-          },
-          start: {
-            character: 11,
-            line: 5,
-          },
-        },
-        severity: DiagnosticSeverity.Warning,
-      },
-    ]);
-  });
-});
diff --git a/src/rulesets/oas3/__tests__/model-description.ts b/src/rulesets/oas3/__tests__/model-description.ts
deleted file mode 100644
index becbcb67..00000000
--- a/src/rulesets/oas3/__tests__/model-description.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { DiagnosticSeverity } from '@stoplight/types';
-import { RuleType, Spectral } from '../../../spectral';
-import * as ruleset from '../index.json';
-
-describe('components-schema-description', () => {
-  const s = new Spectral();
-  s.setRules({
-    'components-schema-description': Object.assign(ruleset.rules['components-schema-description'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['components-schema-description'].type],
-    }),
-  });
-
-  test('validate a correct object', async () => {
-    const results = await s.run({
-      openapi: '3.0.0',
-      paths: {},
-      components: {
-        schemas: {
-          user: {
-            description: 'this describes the user model',
-          },
-        },
-      },
-    });
-    expect(results.length).toEqual(0);
-  });
-
-  test('return errors if a definition is missing description', async () => {
-    const results = await s.run({
-      openapi: '3.0.0',
-      paths: {},
-      components: {
-        schemas: {
-          user: {},
-        },
-      },
-    });
-    expect(results).toEqual([
-      {
-        code: 'components-schema-description',
-        message: 'Model `description` must be present and non-empty string.',
-        path: ['components', 'schemas', 'user'],
-        range: {
-          end: {
-            character: 16,
-            line: 5,
-          },
-          start: {
-            character: 13,
-            line: 5,
-          },
-        },
-        severity: DiagnosticSeverity.Warning,
-      },
-    ]);
-  });
-});
diff --git a/test-harness/README.md b/test-harness/README.md
index ad0deb3f..87bfcabb 100644
--- a/test-harness/README.md
+++ b/test-harness/README.md
@@ -105,7 +105,7 @@ paths: {}
 ====asset:ruleset====
 extends: spectral:oas
 rules:
-  api-servers: error
+  oas3-api-servers: error
 ====command====
 {bin} lint {document} -r {asset:ruleset}
 ====status====
@@ -114,7 +114,7 @@ rules:
 OpenAPI 3.x detected
 
 {document}
- 1:1    error  api-servers       OpenAPI `servers` must be present and non-empty array.
+ 1:1    error  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
  1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
  2:6  warning  info-contact      Info object should contain `contact` object.
  2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/enabled-rules-amount.oas3.scenario b/test-harness/scenarios/enabled-rules-amount.oas3.scenario
index 20e7b609..c24b320e 100644
--- a/test-harness/scenarios/enabled-rules-amount.oas3.scenario
+++ b/test-harness/scenarios/enabled-rules-amount.oas3.scenario
@@ -24,7 +24,7 @@ components:
 ====command====
 {bin} lint {document} --ruleset ./rulesets/parameter-description.oas3.yaml -v
 ====stdout====
-Found 56 rules (1 enabled)
+Found 53 rules (1 enabled)
 Linting {document}
 OpenAPI 3.x detected
 
diff --git a/test-harness/scenarios/results-default-format-json-quiet.oas3.scenario b/test-harness/scenarios/results-default-format-json-quiet.oas3.scenario
index 46f0ea72..be3362d3 100644
--- a/test-harness/scenarios/results-default-format-json-quiet.oas3.scenario
+++ b/test-harness/scenarios/results-default-format-json-quiet.oas3.scenario
@@ -13,7 +13,7 @@ tags:
 ====stdout====
 [
 	{
-		"code": "api-servers",
+		"code": "oas3-api-servers",
 		"path": [],
 		"message": "OpenAPI `servers` must be present and non-empty array.",
 		"severity": 1,
diff --git a/test-harness/scenarios/results-default-format-json.oas3.scenario b/test-harness/scenarios/results-default-format-json.oas3.scenario
index 6552a75a..9ccebd32 100644
--- a/test-harness/scenarios/results-default-format-json.oas3.scenario
+++ b/test-harness/scenarios/results-default-format-json.oas3.scenario
@@ -14,7 +14,7 @@ tags:
 OpenAPI 3.x detected
 [
 	{
-		"code": "api-servers",
+		"code": "oas3-api-servers",
 		"path": [],
 		"message": "OpenAPI `servers` must be present and non-empty array.",
 		"severity": 1,
diff --git a/test-harness/scenarios/results-default-output.oas3.scenario b/test-harness/scenarios/results-default-output.oas3.scenario
index fdc79fa9..99ee87e0 100644
--- a/test-harness/scenarios/results-default-output.oas3.scenario
+++ b/test-harness/scenarios/results-default-output.oas3.scenario
@@ -12,7 +12,7 @@ paths: {}
 ====asset:output.txt====
 ====stdout====
 {document}
- 1:1  warning  api-servers       OpenAPI `servers` must be present and non-empty array.
+ 1:1  warning  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
  1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
  2:6  warning  info-contact      Info object should contain `contact` object.
  2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/results-default.oas3.scenario b/test-harness/scenarios/results-default.oas3.scenario
index 8b0505f0..f37ac7b0 100644
--- a/test-harness/scenarios/results-default.oas3.scenario
+++ b/test-harness/scenarios/results-default.oas3.scenario
@@ -12,7 +12,7 @@ paths: {}
 OpenAPI 3.x detected
 
 {document}
- 1:1  warning  api-servers       OpenAPI `servers` must be present and non-empty array.
+ 1:1  warning  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
  1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
  2:6  warning  info-contact      Info object should contain `contact` object.
  2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/results-format-stylish.oas3.scenario b/test-harness/scenarios/results-format-stylish.oas3.scenario
index 1f8c1b02..4cf53dc8 100644
--- a/test-harness/scenarios/results-format-stylish.oas3.scenario
+++ b/test-harness/scenarios/results-format-stylish.oas3.scenario
@@ -12,7 +12,7 @@ paths: {}
 OpenAPI 3.x detected
 
 {document}
- 1:1  warning  api-servers       OpenAPI `servers` must be present and non-empty array.
+ 1:1  warning  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
  1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
  2:6  warning  info-contact      Info object should contain `contact` object.
  2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/results-skip-rule.oas3.scenario b/test-harness/scenarios/results-skip-rule.oas3.scenario
index cd0b2b24..1e38b335 100644
--- a/test-harness/scenarios/results-skip-rule.oas3.scenario
+++ b/test-harness/scenarios/results-skip-rule.oas3.scenario
@@ -12,7 +12,7 @@ paths: {}
 OpenAPI 3.x detected
 
 {document}
- 1:1  warning  api-servers       OpenAPI `servers` must be present and non-empty array.
+ 1:1  warning  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
  1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
  2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.
 
diff --git a/test-harness/scenarios/results-skip-rules-multiple.oas3.scenario b/test-harness/scenarios/results-skip-rules-multiple.oas3.scenario
index 35c9a0ae..bc686f08 100644
--- a/test-harness/scenarios/results-skip-rules-multiple.oas3.scenario
+++ b/test-harness/scenarios/results-skip-rules-multiple.oas3.scenario
@@ -12,7 +12,7 @@ paths: {}
 OpenAPI 3.x detected
 
 {document}
- 1:1  warning  api-servers   OpenAPI `servers` must be present and non-empty array.
- 1:1  warning  openapi-tags  OpenAPI object should have non-empty `tags` array.
+ 1:1  warning  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
+ 1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
 
 ✖ 2 problems (0 errors, 2 warnings, 0 infos, 0 hints)
diff --git a/test-harness/scenarios/rulesets/parameter-description.oas2.yaml b/test-harness/scenarios/rulesets/parameter-description.oas2.yaml
index dc82fa3a..773a2473 100644
--- a/test-harness/scenarios/rulesets/parameter-description.oas2.yaml
+++ b/test-harness/scenarios/rulesets/parameter-description.oas2.yaml
@@ -1,3 +1,3 @@
-extends: [[spectral:oas2, off]]
+extends: [[spectral:oas, off]]
 rules:
   oas2-parameter-description: true
diff --git a/test-harness/scenarios/rulesets/parameter-description.oas3.yaml b/test-harness/scenarios/rulesets/parameter-description.oas3.yaml
index dbc1f0fd..f2a50aca 100644
--- a/test-harness/scenarios/rulesets/parameter-description.oas3.yaml
+++ b/test-harness/scenarios/rulesets/parameter-description.oas3.yaml
@@ -1,3 +1,3 @@
-extends: [[spectral:oas3, off]]
+extends: [[spectral:oas, off]]
 rules:
   oas3-parameter-description: true
diff --git a/test-harness/scenarios/severity/display-warnings.oas3.scenario b/test-harness/scenarios/severity/display-warnings.oas3.scenario
index f573dd14..2f75fc35 100644
--- a/test-harness/scenarios/severity/display-warnings.oas3.scenario
+++ b/test-harness/scenarios/severity/display-warnings.oas3.scenario
@@ -47,7 +47,7 @@ paths:
 OpenAPI 3.x detected
 
 {document}
-  1:1   warning  api-servers            OpenAPI `servers` must be present and non-empty array.
+  1:1   warning  oas3-api-servers       OpenAPI `servers` must be present and non-empty array.
   1:1   warning  openapi-tags           OpenAPI object should have non-empty `tags` array.
   2:6   warning  info-contact           Info object should contain `contact` object.
   2:6   warning  info-description       OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/severity/fail-on-error-no-error.scenario b/test-harness/scenarios/severity/fail-on-error-no-error.scenario
index 67aecd83..72d537db 100644
--- a/test-harness/scenarios/severity/fail-on-error-no-error.scenario
+++ b/test-harness/scenarios/severity/fail-on-error-no-error.scenario
@@ -25,7 +25,7 @@ paths:
 OpenAPI 3.x detected
 
 {document}
-  1:1   warning  api-servers            OpenAPI `servers` must be present and non-empty array.
+  1:1   warning  oas3-api-servers       OpenAPI `servers` must be present and non-empty array.
   1:1   warning  openapi-tags           OpenAPI object should have non-empty `tags` array.
   2:6   warning  info-contact           Info object should contain `contact` object.
   2:6   warning  info-description       OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/severity/fail-on-error.oas3.scenario b/test-harness/scenarios/severity/fail-on-error.oas3.scenario
index 1fecf145..31768cef 100644
--- a/test-harness/scenarios/severity/fail-on-error.oas3.scenario
+++ b/test-harness/scenarios/severity/fail-on-error.oas3.scenario
@@ -25,7 +25,7 @@ paths:
 OpenAPI 3.x detected
 
 {document}
-  1:1   warning  api-servers                   OpenAPI `servers` must be present and non-empty array.
+  1:1   warning  oas3-api-servers              OpenAPI `servers` must be present and non-empty array.
   1:1   warning  openapi-tags                  OpenAPI object should have non-empty `tags` array.
   2:6   warning  info-contact                  Info object should contain `contact` object.
   2:6   warning  info-description              OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/severity/stylish-display-proper-names.scenario b/test-harness/scenarios/severity/stylish-display-proper-names.scenario
index 506f5e53..4fcd7873 100644
--- a/test-harness/scenarios/severity/stylish-display-proper-names.scenario
+++ b/test-harness/scenarios/severity/stylish-display-proper-names.scenario
@@ -110,7 +110,7 @@ The name of severity levels are display correctly by stylish formatter
 OpenAPI 3.x detected
 
 {document}
-  1:1       warning  api-servers             OpenAPI `servers` must be present and non-empty array.
+  1:1       warning  oas3-api-servers        OpenAPI `servers` must be present and non-empty array.
   1:1       warning  openapi-tags            OpenAPI object should have non-empty `tags` array.
   3:10        error  info-contact            Info object should contain `contact` object.
   3:10      warning  info-description        OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/stdin-document-with-errors.scenario b/test-harness/scenarios/stdin-document-with-errors.scenario
index bf5cb9d7..9f802b53 100644
--- a/test-harness/scenarios/stdin-document-with-errors.scenario
+++ b/test-harness/scenarios/stdin-document-with-errors.scenario
@@ -9,7 +9,7 @@ paths: {}
 ====asset:ruleset====
 extends: spectral:oas
 rules:
-  api-servers: error
+  oas3-api-servers: error
 ====command====
 cat {document} | {bin} lint -r {asset:ruleset}
 ====status====
@@ -18,7 +18,7 @@ cat {document} | {bin} lint -r {asset:ruleset}
 OpenAPI 3.x detected
 
 <STDIN>
- 1:1    error  api-servers       OpenAPI `servers` must be present and non-empty array.
+ 1:1    error  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
  1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
  2:6  warning  info-contact      Info object should contain `contact` object.
  2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.
diff --git a/test-harness/scenarios/stdin.scenario b/test-harness/scenarios/stdin.scenario
index f62bfd86..fb56fd06 100644
--- a/test-harness/scenarios/stdin.scenario
+++ b/test-harness/scenarios/stdin.scenario
@@ -12,7 +12,7 @@ cat {document} | {bin} lint
 OpenAPI 3.x detected
 
 <STDIN>
- 1:1  warning  api-servers       OpenAPI `servers` must be present and non-empty array.
+ 1:1  warning  oas3-api-servers  OpenAPI `servers` must be present and non-empty array.
  1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
  2:6  warning  info-contact      Info object should contain `contact` object.
  2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.