Repository: Redocly/redocly-cli
Pull request #662 (2022-04-28 13:10:17)

feat: adjust assertions syntax
## What/Why/How?

Simplify the assertions syntax by getting rid of the nested arrays.

The code changes are only these few lines: https://github.com/Redocly/openapi-cli/pull/662/files#diff-47607adc7025266f655cda3df6abbd5cc9f1fbf4e9f493c7b0ba6ed6c3350614R716-R738

The rest is examples and docs changes.

This change basically collects all of the assertions under `rules` and puts them in an `assertions` rule. Thus only changing the definition in `.redocly.yaml` and not the internal handling. 

## Reference

https://github.com/Redocly/openapi-cli/issues/647

## Testing

## Screenshots (optional)

## Check yourself

- [ ] Code is linted
- [ ] Tested with redoc/reference-docs/workflows
- [ ] All new/updated code is covered with tests

## Security

- [ ] Security impact of change has been considered
- [ ] Code follows company security practices and guidelines



Patch:
diff --git a/docs/resources/rules/assertions.md b/docs/resources/rules/assertions.md
index 0edf0112..55a71b8d 100644
--- a/docs/resources/rules/assertions.md
+++ b/docs/resources/rules/assertions.md
@@ -11,12 +11,15 @@ Follow or comment in the issue for more details.
 
 Configure assertions to enforce your API design standards without coding custom rules.
 
-Define `assertions` in the rules map of the `lint` section in the Redocly configuration file.
+Define assertions in the `rules` map of the `lint` section in the Redocly configuration file.
 
 ```yaml
 lint:
   rules:
-    assertions: ...
+    assert/assertion-name:
+      ...
+    assert/one-more-assertion-name:
+      ...
 ```
 
 An assertion describes the contents that the linter expects to find in your API definition. During the validation process, the linter goes through your API definition and checks if its contents match the expectations. If something was described in an assertion, but the API definition doesn't correspond to the description, the linter shows you a warning or error message in the log.
@@ -24,7 +27,7 @@ An assertion describes the contents that the linter expects to find in your API
 
 Property | Type | Description
 -- | -- | --
-assertions | [[Assertion object]](#assertion-object) | A list of assertions to enforce your custom API design standards. Add or edit your assertions in the configuration file. The `assertions` rule is composed of a list of `assertion` objects. This rule may result in more than one problem message, as you may define more than one assertion. More than one assertion may be defined in the list, and any assertion may have multiple asserts.
+assert/my-assertion-name | [Assertion object](#assertion-object) | An assertion definition to enforce your custom API design standards. Add or edit your assertions in the configuration file. The assertion is a rule that starts with a `assert/` prefix followed by a unique assertion rule name. Assertion name is displayed in the lint log if the assertion fails. More than one assertion may be defined, and any assertion may have multiple asserts.
 
 ## Assertion object
 
@@ -36,7 +39,6 @@ context | [Context object](#context-object) | The context influences evaluation
 message | `string` | Optional problem message displayed if the assertion is false. If omitted, the default message "The assertion doesn't meet required conditions" will be displayed.
 suggest | [`string`] | Optional list of suggestions to display if the problem occurs.
 severity | `string` | Optional field to configure the severity level of the problem if the assertion is false. It must be one of these values: `error`, `warn`, `off`. Default value is `error`.
-assertionId | `string` | ID displayed in the lint log if the assertion fails. Useful for troubleshooting and identifying the assertion.
 enum | [`string`] | Asserts a value is within a predefined list of values. See [enum example](#enum-example).
 pattern | `string` | Asserts a value matches a regex pattern. See [regex pattern example](#pattern-example).
 casing | `string` | Asserts a casing style. Supported styles are: `camelCase`, `kebab-case`, `snake_case`, `PascalCase`, `MACRO_CASE`, `COBOL-CASE`, `flatcase`. See [casing example](#casing-example).
@@ -79,36 +81,39 @@ The following example shows how to configure those assertions:
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Tag
-        property: description
-        message: Tag description must be at least 30 characters and end with a full stop.
-        severity: error
-        defined: true
-        minLength: 30
-        pattern: /\.$/
-      - subject: Operation
-        property: description
-        message: Operation description must be at least 30 characters and end with a full stop.
-        severity: error
-        defined: true
-        minLength: 30
-        pattern: /\.$/
-      - subject: Info
-        property: description
-        message: Info description must be at least 30 characters and end with a full stop.
-        severity: error
-        defined: true
-        minLength: 30
-        pattern: /\.$/
-      - subject: Operation
-        property: summary
-        message: Operation summary must be between 20 and 60 characters and not end with a full stop.
-        severity: error
-        defined: true
-        minLength: 20
-        maxLength: 60
-        pattern: /[^\.]$/
+    assert/tag-description:
+      subject: Tag
+      property: description
+      message: Tag description must be at least 30 characters and end with a full stop.
+      severity: error
+      defined: true
+      minLength: 30
+      pattern: /\.$/
+    assert/operation-description:
+      subject: Operation
+      property: description
+      message: Operation description must be at least 30 characters and end with a full stop.
+      severity: error
+      defined: true
+      minLength: 30
+      pattern: /\.$/
+    assert/info-description:
+      subject: Info
+      property: description
+      message: Info description must be at least 30 characters and end with a full stop.
+      severity: error
+      defined: true
+      minLength: 30
+      pattern: /\.$/
+    assert/operation-summary:
+      subject: Operation
+      property: summary
+      message: Operation summary must be between 20 and 60 characters and not end with a full stop.
+      severity: error
+      defined: true
+      minLength: 20
+      maxLength: 60
+      pattern: /[^\.]$/
 ```
 
 ### `property` example
@@ -118,11 +123,11 @@ The following example asserts that every path item has a GET operation defined.
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: PathItem
-        property: get
-        message: Every path item must have a GET operation.
-        defined: true
+    assert/path-item-get-operation-defined:
+      subject: PathItem
+      property: get
+      message: Every path item must have a GET operation.
+      defined: true
 ```
 
 A different way to declare the same assertion is to require that the `PathItem` has the `get` key.
@@ -131,11 +136,11 @@ Notice we don't need to include `property` in this approach.
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: PathItem
-        message: Every path item must have a GET operation.
-        required:
-          - get
+    assert/path-item-operation-required:
+      subject: PathItem
+      message: Every path item must have a GET operation.
+      required:
+        - get
 ```
 
 The following example asserts that Tags have both name and description defined.
@@ -143,13 +148,13 @@ The following example asserts that Tags have both name and description defined.
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Tag
-        property:
-          - name
-          - description
-        message: Every tag must have a name and description.
-        defined: true
+    assert/tag-name-and-desc-defined:
+      subject: Tag
+      property:
+        - name
+        - description
+      message: Every tag must have a name and description.
+      defined: true
 ```
 
 Another way to compose that rule is to require the subject keys:
@@ -157,12 +162,12 @@ Another way to compose that rule is to require the subject keys:
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Tag
-        message: Every tag must have a name and description.
-        required:
-          - name
-          - description
+    assert/tag-name-and-desc-required:
+      subject: Tag
+      message: Every tag must have a name and description.
+      required:
+        - name
+        - description
 ```
 
 ### `context` example
@@ -174,14 +179,14 @@ Without the `context`, the assertion would evaluate every MediaTypeMap including
 To restrict the evaluation, use the `context` feature to limit what will be evaluated.
 
 ```yaml
-assertions:
-  - context:
-    - type: Operation
-      matchParentKeys: [put]
-    - type: Response
-      matchParentKeys: ['201', '200']
-    subject: MediaTypeMap
-    disallowed: ['application/pdf']
+assert/no-pdf-in-ok-response:
+  context:
+  - type: Operation
+    matchParentKeys: [put]
+  - type: Response
+    matchParentKeys: ['201', '200']
+  subject: MediaTypeMap
+  disallowed: ['application/pdf']
 ```
 
 ### `enum` example
@@ -191,12 +196,12 @@ The following example asserts that only `application/json` can be used as a key
 ```yaml keys
 lint:
   rules:
-    assertions:
-      - subject: MediaTypeMap
-        message: Only application/json can be used
-        severity: error
-        enum:
-          - application/json
+    assert/media-type-map-application-json:
+      subject: MediaTypeMap
+      message: Only application/json can be used
+      severity: error
+      enum:
+        - application/json
 ```
 
 The following example asserts that the operation summary must match one of the listed enums.
@@ -204,17 +209,17 @@ The following example asserts that the operation summary must match one of the l
 ```yaml values
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Summary must be one of the predefined values
-        suggest:
-          - change to 'My resource'
-          - change to 'My collection'
-        severity: error
-        enum:
-          - My resource
-          - My collection
+    assert/operation-summary-match:
+      subject: Operation
+      property: summary
+      message: Summary must be one of the predefined values
+      suggest:
+        - change to 'My resource'
+        - change to 'My collection'
+      severity: error
+      enum:
+        - My resource
+        - My collection
 ```
 
 ### `pattern` example
@@ -224,12 +229,12 @@ The following example asserts that the operation summary contains "test".
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Summary should match a regex
-        severity: error
-        pattern: /test/
+    assert/operation-summary-contains-test:
+      subject: Operation
+      property: summary
+      message: Summary should match a regex
+      severity: error
+      pattern: /test/
 ```
 
 ### `casing` example
@@ -239,11 +244,11 @@ The following example asserts the casing style is `PascalCase` for NamedExamples
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: NamedExamples
-        message: NamedExamples key must be in PascalCase
-        severity: error
-        casing: PascalCase
+    assert/named-examples-pascal-case:
+      subject: NamedExamples
+      message: NamedExamples key must be in PascalCase
+      severity: error
+      casing: PascalCase
 ```
 
 Casing supports the following styles:
@@ -264,13 +269,13 @@ This assertion evaluates only property keys for the node, but not property value
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        message: "Operation must not define both properties together: description and externalDocs"
-        severity: error
-        mutuallyExclusive:
-          - description
-          - externalDocs
+    assert/operation-no-both-description-and-external-docs:
+      subject: Operation
+      message: "Operation must not define both properties together: description and externalDocs"
+      severity: error
+      mutuallyExclusive:
+        - description
+        - externalDocs
 ```
 
 ### `mutuallyRequired` example
@@ -281,13 +286,13 @@ This assertion evaluates only property keys for the node, but not property value
 ```yaml Schema example
 lint:
   rules:
-    assertions:
-      - subject: SchemaProperties
-        message: The created_at and updated_at properties are mutually required
-        severity: error
-        mutuallyRequired:
-          - created_at
-          - updated_at
+    assert/schema-properties-both-created-at-and-updated-at:
+      subject: SchemaProperties
+      message: The created_at and updated_at properties are mutually required
+      severity: error
+      mutuallyRequired:
+        - created_at
+        - updated_at
 ```
 
 The following example asserts that when `PUT` requests have either `200` or `201` defined, both `200` and `201` responses must be defined.
@@ -295,17 +300,17 @@ The following example asserts that when `PUT` requests have either `200` or `201
 ```yaml Response example
 lint:
   rules:
-    assertions:
-      - subject: ResponsesMap
-        context:
-          - type: Operation
-            matchParentKeys:
-              - put
-        message: Must mutually define 200 and 201 responses for PUT requests.
-        severity: error
-        mutuallyRequired:
-          - '200'
-          - '201'
+    assert/put-200-and-201:
+      subject: ResponsesMap
+      context:
+        - type: Operation
+          matchParentKeys:
+            - put
+      message: Must mutually define 200 and 201 responses for PUT requests.
+      severity: error
+      mutuallyRequired:
+        - '200'
+        - '201'
 ```
 
 ### `required` example
@@ -316,17 +321,17 @@ The difference between `mutuallyRequired` is that neither `200` and `201` need t
 ```yaml Response example
 lint:
   rules:
-    assertions:
-      - subject: ResponseMap
-        context:
-          - type: Operation
-            matchParentKeys:
-              - put
-        message: Must define 200 and 201 responses for PUT requests.
-        severity: error
-        required:
-          - '200'
-          - '201'
+    assert/put-200-and-201:
+      subject: ResponseMap
+      context:
+        - type: Operation
+          matchParentKeys:
+            - put
+      message: Must define 200 and 201 responses for PUT requests.
+      severity: error
+      required:
+        - '200'
+        - '201'
 ```
 
 ### `disallowed` example
@@ -336,13 +341,13 @@ The following example asserts that `x-code-samples` and `x-internal` are not def
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        message: x-code-samples and x-internal must not be defined
-        severity: error
-        disallowed:
-          - x-code-samples
-          - x-internal
+    assert/no-x-code-samples-and-x-internal:
+      subject: Operation
+      message: x-code-samples and x-internal must not be defined
+      severity: error
+      disallowed:
+        - x-code-samples
+        - x-internal
 ```
 
 ### `defined` example
@@ -352,12 +357,12 @@ The following example asserts that `x-codeSamples` is defined.
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: x-codeSamples
-        message: x-codeSamples must be defined
-        severity: error
-        defined: true
+    assert/x-code-samples-defined:
+      subject: Operation
+      property: x-codeSamples
+      message: x-codeSamples must be defined
+      severity: error
+      defined: true
 ```
 
 ### `undefined` example
@@ -367,14 +372,14 @@ The following example asserts that `x-code-samples` is undefined.
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: x-code-samples
-        message: x-code-samples is deprecated
-        suggest:
-          - x-codeSamples instead of x-code-samples
-        severity: error
-        undefined: true
+    assert/x-code-samples-undefined:
+      subject: Operation
+      property: x-code-samples
+      message: x-code-samples is deprecated
+      suggest:
+        - x-codeSamples instead of x-code-samples
+      severity: error
+      undefined: true
 ```
 
 ### `nonEmpty` example
@@ -384,12 +389,12 @@ The following example asserts that the operation summary is not empty.
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Operation summary should not be empty
-        severity: error
-        nonEmpty: true
+    assert/operation-summary-non-empty:
+      subject: Operation
+      property: summary
+      message: Operation summary should not be empty
+      severity: error
+      nonEmpty: true
 ```
 
 ### `minLength` example
@@ -399,12 +404,12 @@ The following example asserts that the minimum length of each operation summary
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Operation summary must have minimum of 20 chars length
-        severity: error
-        minLength: 20
+    assert/operation-summary-min-length:
+      subject: Operation
+      property: summary
+      message: Operation summary must have minimum of 20 chars length
+      severity: error
+      minLength: 20
 ```
 
 ### `maxLength` example
@@ -414,12 +419,12 @@ The following example asserts that the maximum length of each operation summary
 ```yaml
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Operation summary must have a maximum of 20 characters
-        severity: error
-        maxLength: 20
+    assert/operation-summary-max-length:
+      subject: Operation
+      property: summary
+      message: Operation summary must have a maximum of 20 characters
+      severity: error
+      maxLength: 20
 ```
 Redocly CLI
 ## OpenAPI node types
diff --git a/packages/core/src/config/config-resolvers.ts b/packages/core/src/config/config-resolvers.ts
index 52b317e6..1a92d65a 100644
--- a/packages/core/src/config/config-resolvers.ts
+++ b/packages/core/src/config/config-resolvers.ts
@@ -11,7 +11,14 @@ import {
   prefixRules,
   transformConfig,
 } from './utils';
-import type { LintRawConfig, Plugin, RawConfig, ResolvedApi, ResolvedLintConfig } from './types';
+import type {
+  LintRawConfig,
+  Plugin,
+  RawConfig,
+  ResolvedApi,
+  ResolvedLintConfig,
+  RuleConfig,
+} from './types';
 import { isNotString, isString, notUndefined, parseYaml } from '../utils';
 import { Config } from './config';
 
@@ -175,7 +182,7 @@ export async function resolveApis({
   return resolvedApis;
 }
 
-export async function resolveLint(
+async function resolveAndMergeNestedLint(
   {
     lintConfig,
     configPath = '',
@@ -213,7 +220,7 @@ export async function resolveLint(
         ? new URL(presetItem, configPath).href
         : path.resolve(path.dirname(configPath), presetItem);
       const extendedLintConfig = await loadExtendLintConfig(pathItem, resolver);
-      return await resolveLint(
+      return await resolveAndMergeNestedLint(
         {
           lintConfig: extendedLintConfig,
           configPath: pathItem,
@@ -245,6 +252,23 @@ export async function resolveLint(
   };
 }
 
+export async function resolveLint(
+  lintOpts: {
+    lintConfig?: LintRawConfig;
+    configPath?: string;
+    resolver?: BaseResolver;
+  },
+  parentConfigPaths: string[] = [],
+  extendPaths: string[] = [],
+): Promise<ResolvedLintConfig> {
+  const resolvedLint = await resolveAndMergeNestedLint(lintOpts, parentConfigPaths, extendPaths);
+
+  return {
+    ...resolvedLint,
+    rules: resolvedLint.rules && groupLintAssertionRules(resolvedLint.rules),
+  };
+}
+
 export function resolvePreset(presetName: string, plugins: Plugin[]): ResolvedLintConfig {
   const { pluginId, configName } = parsePresetName(presetName);
   const plugin = plugins.find((p) => p.id === pluginId);
@@ -302,3 +326,34 @@ function getMergedLintRawConfig(configLint: LintRawConfig, apiLint?: LintRawConf
   };
   return resultLint;
 }
+
+function groupLintAssertionRules(
+  rules: Record<string, RuleConfig> | undefined,
+): Record<string, RuleConfig> | undefined {
+  if (!rules) {
+    return rules;
+  }
+
+  // Create a new record to avoid mutating original
+  const transformedRules: Record<string, RuleConfig> = {};
+
+  // Collect assertion rules
+  const assertions = [];
+  for (const [ruleKey, rule] of Object.entries(rules)) {
+    if (ruleKey.startsWith('assert/') && typeof rule === 'object' && rule !== null) {
+      const assertion = rule;
+      assertions.push({
+        ...assertion,
+        assertionId: ruleKey.replace('assert/', ''),
+      });
+    } else {
+      // If it's not an assertion, keep it as is
+      transformedRules[ruleKey] = rule;
+    }
+  }
+  if (assertions.length > 0) {
+    transformedRules.assertions = assertions;
+  }
+
+  return transformedRules;
+}


Test patch:
diff --git a/__tests__/lint/assertions-casing-camel-case-error/.redocly.yaml b/__tests__/lint/assertions-casing-camel-case-error/.redocly.yaml
index 08196a14..51437c0f 100644
--- a/__tests__/lint/assertions-casing-camel-case-error/.redocly.yaml
+++ b/__tests__/lint/assertions-casing-camel-case-error/.redocly.yaml
@@ -4,16 +4,17 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-        - type: Operation
-          matchParentKeys: [get]
-        subject: Operation
-        property: operationId
-        message: Operation id for get operation should be canmelCase
-        casing: camelCase
-      - subject: Operation
-        property: operationId
-        message: Operation id should be camelCase
-        casing: camelCase
+    assert/operation-get:
+      context:
+      - type: Operation
+        matchParentKeys: [get]
+      subject: Operation
+      property: operationId
+      message: Operation id for get operation should be camelCase
+      casing: camelCase
+    assert/operation-id-camel-case:
+      subject: Operation
+      property: operationId
+      message: Operation id should be camelCase
+      casing: camelCase
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-casing-camel-case-error/snapshot.js b/__tests__/lint/assertions-casing-camel-case-error/snapshot.js
index 324975e2..ee0ec6b5 100644
--- a/__tests__/lint/assertions-casing-camel-case-error/snapshot.js
+++ b/__tests__/lint/assertions-casing-camel-case-error/snapshot.js
@@ -5,7 +5,7 @@ exports[`E2E lint assertions-casing-camel-case-error 1`] = `
 validating /openapi.yaml...
 [1] openapi.yaml:19:20 at #/paths/~1pet~1findByStatus/get/operationId
 
-Operation id for get operation should be canmelCase
+Operation id for get operation should be camelCase
 
 17 | /pet/findByStatus:
 18 |   get:
@@ -14,7 +14,7 @@ Operation id for get operation should be canmelCase
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the operation-get assertion rule.
 
 
 [2] openapi.yaml:19:20 at #/paths/~1pet~1findByStatus/get/operationId
@@ -28,7 +28,7 @@ Operation id should be camelCase
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-camel-case assertion rule.
 
 
 [3] openapi.yaml:31:20 at #/paths/~1pet~1findByStatus/post/operationId
@@ -42,7 +42,7 @@ Operation id should be camelCase
 32 |   summary: summary example
 33 |   responses:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-camel-case assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-casing-kebab-case-error/.redocly.yaml b/__tests__/lint/assertions-casing-kebab-case-error/.redocly.yaml
index 0e9bd14e..d8d473bd 100644
--- a/__tests__/lint/assertions-casing-kebab-case-error/.redocly.yaml
+++ b/__tests__/lint/assertions-casing-kebab-case-error/.redocly.yaml
@@ -4,16 +4,17 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-          - type: Operation
-            matchParentKeys: [get]
-        subject: Operation
-        property: operationId
-        message: Operation id should be kebab-case
-        casing: kebab-case
-      - subject: Operation
-        property: operationId
-        message: Operation id should be camelCase
-        casing: kebab-case
+    assert/operation-get:
+      context:
+        - type: Operation
+          matchParentKeys: [get]
+      subject: Operation
+      property: operationId
+      message: Operation id should be kebab-case
+      casing: kebab-case
+    assert/operation-id-kebab-case:
+      subject: Operation
+      property: operationId
+      message: Operation id should be kebab-case
+      casing: kebab-case
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-casing-kebab-case-error/snapshot.js b/__tests__/lint/assertions-casing-kebab-case-error/snapshot.js
index 20a8e9ac..2ce40435 100644
--- a/__tests__/lint/assertions-casing-kebab-case-error/snapshot.js
+++ b/__tests__/lint/assertions-casing-kebab-case-error/snapshot.js
@@ -14,12 +14,12 @@ Operation id should be kebab-case
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the operation-get assertion rule.
 
 
 [2] openapi.yaml:19:20 at #/paths/~1pet~1findByStatus/get/operationId
 
-Operation id should be camelCase
+Operation id should be kebab-case
 
 17 | /pet/findByStatus:
 18 |   get:
@@ -28,12 +28,12 @@ Operation id should be camelCase
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-kebab-case assertion rule.
 
 
 [3] openapi.yaml:31:20 at #/paths/~1pet~1findByStatus/post/operationId
 
-Operation id should be camelCase
+Operation id should be kebab-case
 
 29 |       description: example description
 30 | post:
@@ -42,7 +42,7 @@ Operation id should be camelCase
 32 |   summary: summary example
 33 |   responses:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-kebab-case assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-casing-pascal-case-error/.redocly.yaml b/__tests__/lint/assertions-casing-pascal-case-error/.redocly.yaml
index ca8ddd84..6122548c 100644
--- a/__tests__/lint/assertions-casing-pascal-case-error/.redocly.yaml
+++ b/__tests__/lint/assertions-casing-pascal-case-error/.redocly.yaml
@@ -4,16 +4,17 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-          - type: Operation
-            matchParentKeys: [get]
-        subject: Operation
-        property: operationId
-        message: Operation id should be PascalCase
-        casing: PascalCase
-      - subject: Operation
-        property: operationId
-        message: Operation id should be camelCase
-        casing: PascalCase
+    assert/operation-get:
+      context:
+        - type: Operation
+          matchParentKeys: [get]
+      subject: Operation
+      property: operationId
+      message: Operation id should be PascalCase
+      casing: PascalCase
+    assert/operation-id-pascal-case:
+      subject: Operation
+      property: operationId
+      message: Operation id should be PascalCase
+      casing: PascalCase
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-casing-pascal-case-error/snapshot.js b/__tests__/lint/assertions-casing-pascal-case-error/snapshot.js
index c9735eab..978c0124 100644
--- a/__tests__/lint/assertions-casing-pascal-case-error/snapshot.js
+++ b/__tests__/lint/assertions-casing-pascal-case-error/snapshot.js
@@ -14,12 +14,12 @@ Operation id should be PascalCase
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the operation-get assertion rule.
 
 
 [2] openapi.yaml:19:20 at #/paths/~1pet~1findByStatus/get/operationId
 
-Operation id should be camelCase
+Operation id should be PascalCase
 
 17 | /pet/findByStatus:
 18 |   get:
@@ -28,12 +28,12 @@ Operation id should be camelCase
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-pascal-case assertion rule.
 
 
 [3] openapi.yaml:31:20 at #/paths/~1pet~1findByStatus/post/operationId
 
-Operation id should be camelCase
+Operation id should be PascalCase
 
 29 |       description: example description
 30 | post:
@@ -42,7 +42,7 @@ Operation id should be camelCase
 32 |   summary: summary example
 33 |   responses:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-pascal-case assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-casing-snake-case-error/.redocly.yaml b/__tests__/lint/assertions-casing-snake-case-error/.redocly.yaml
index 1ec5ef6b..83225169 100644
--- a/__tests__/lint/assertions-casing-snake-case-error/.redocly.yaml
+++ b/__tests__/lint/assertions-casing-snake-case-error/.redocly.yaml
@@ -4,16 +4,17 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-          - type: Operation
-            matchParentKeys: [get]
-        subject: Operation
-        property: operationId
-        message: Operation id should be snake_case
-        casing: PascalCase
-      - subject: Operation
-        property: operationId
-        message: Operation id should be snake_case
-        casing: snake_case
+    assert/operation-id-pascal-case:
+      context:
+        - type: Operation
+          matchParentKeys: [get]
+      subject: Operation
+      property: operationId
+      message: Operation id for get requests should be PascalCase
+      casing: PascalCase
+    assert/operation-id-snake-case:
+      subject: Operation
+      property: operationId
+      message: Operation id should be snake_case
+      casing: snake_case
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-casing-snake-case-error/snapshot.js b/__tests__/lint/assertions-casing-snake-case-error/snapshot.js
index 4244faef..4339d05a 100644
--- a/__tests__/lint/assertions-casing-snake-case-error/snapshot.js
+++ b/__tests__/lint/assertions-casing-snake-case-error/snapshot.js
@@ -5,7 +5,7 @@ exports[`E2E lint assertions-casing-snake-case-error 1`] = `
 validating /openapi.yaml...
 [1] openapi.yaml:19:20 at #/paths/~1pet~1findByStatus/get/operationId
 
-Operation id should be snake_case
+Operation id for get requests should be PascalCase
 
 17 | /pet/findByStatus:
 18 |   get:
@@ -14,7 +14,7 @@ Operation id should be snake_case
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the operation-id-pascal-case assertion rule.
 
 
 [2] openapi.yaml:19:20 at #/paths/~1pet~1findByStatus/get/operationId
@@ -28,7 +28,7 @@ Operation id should be snake_case
 20 |     summary: summary example
 21 |     tags:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-snake-case assertion rule.
 
 
 [3] openapi.yaml:31:20 at #/paths/~1pet~1findByStatus/post/operationId
@@ -42,7 +42,7 @@ Operation id should be snake_case
 32 |   summary: summary example
 33 |   responses:
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-id-snake-case assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-defined-error/.redocly.yaml b/__tests__/lint/assertions-defined-error/.redocly.yaml
index fb60209a..a1143f42 100644
--- a/__tests__/lint/assertions-defined-error/.redocly.yaml
+++ b/__tests__/lint/assertions-defined-error/.redocly.yaml
@@ -4,16 +4,17 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-        - type: Response
-          excludeParentKeys: ['200']
-        subject: Response
-        property: content
-        message: Response content should be defined
-        defined: true
-      - subject: Operation
-        property: security
-        message: Operation security should be defined
-        defined: true
+    assert/response-content-defined:
+      context:
+      - type: Response
+        excludeParentKeys: ['204']
+      subject: Response
+      property: content
+      message: Response content should be defined
+      defined: true
+    assert/operation-security:
+      subject: Operation
+      property: security
+      message: Operation security should be defined
+      defined: true
   extends: []
diff --git a/__tests__/lint/assertions-defined-error/snapshot.js b/__tests__/lint/assertions-defined-error/snapshot.js
index f6c202a4..fb07e313 100644
--- a/__tests__/lint/assertions-defined-error/snapshot.js
+++ b/__tests__/lint/assertions-defined-error/snapshot.js
@@ -18,7 +18,7 @@ Operation security should be defined
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 34 |
 
-Error was generated by the assertion #2 rule.
+Error was generated by the operation-security assertion rule.
 
 
 [2] openapi.yaml:33:11 at #/paths/~1pet~1findByStatus/get/responses/404/content
@@ -31,7 +31,7 @@ Response content should be defined
    |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 34 |
 
-Error was generated by the assertion #1 rule.
+Error was generated by the response-content-defined assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-enum-error/.redocly.yaml b/__tests__/lint/assertions-enum-error/.redocly.yaml
index 77194bc6..1b567185 100644
--- a/__tests__/lint/assertions-enum-error/.redocly.yaml
+++ b/__tests__/lint/assertions-enum-error/.redocly.yaml
@@ -4,15 +4,15 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject:
-        - Operation
-        property: summary
-        message: Operation summary value should be among of predefined values
-        enum:
-          - test summary
-          - test example
-        suggest:
-          - test summary
-          - test example
+    assert/operation-summary-value:
+      subject:
+      - Operation
+      property: summary
+      message: Operation summary value should be among of predefined values
+      enum:
+        - test summary
+        - test example
+      suggest:
+        - test summary
+        - test example
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-enum-error/snapshot.js b/__tests__/lint/assertions-enum-error/snapshot.js
index c312a43b..ac0a2405 100644
--- a/__tests__/lint/assertions-enum-error/snapshot.js
+++ b/__tests__/lint/assertions-enum-error/snapshot.js
@@ -18,7 +18,7 @@ Did you mean:
 21 |   tags:
 22 |     - foo
 
-Error was generated by the assertion #1 rule.
+Error was generated by the operation-summary-value assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-enum-on-keys-error/.redocly.yaml b/__tests__/lint/assertions-enum-on-keys-error/.redocly.yaml
index 849b85ee..88d85d99 100644
--- a/__tests__/lint/assertions-enum-on-keys-error/.redocly.yaml
+++ b/__tests__/lint/assertions-enum-on-keys-error/.redocly.yaml
@@ -4,10 +4,10 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject:
-        - MediaTypeMap
-        message: Only application/json can be used
-        enum:
-          - application/json
+    assert/media-type-application-json:
+      subject:
+      - MediaTypeMap
+      message: Only application/json can be used
+      enum:
+        - application/json
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-enum-on-keys-error/snapshot.js b/__tests__/lint/assertions-enum-on-keys-error/snapshot.js
index a0d26503..0c77f653 100644
--- a/__tests__/lint/assertions-enum-on-keys-error/snapshot.js
+++ b/__tests__/lint/assertions-enum-on-keys-error/snapshot.js
@@ -14,7 +14,7 @@ Only application/json can be used
 29 |     application/json:
 30 |       schema:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the media-type-application-json assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-error/.redocly.yaml b/__tests__/lint/assertions-error/.redocly.yaml
index cce75cc8..0f882a16 100644
--- a/__tests__/lint/assertions-error/.redocly.yaml
+++ b/__tests__/lint/assertions-error/.redocly.yaml
@@ -6,130 +6,147 @@ lint:
   extends: 
     - recommended
   rules:
-    assertions:
-      - subject: Tag
-        property: description
-        message: Tag description must be at least 13 characters and end with a full stop.
-        severity: error
-        minLength: 13
-        pattern: /\.$/
+    assert/tag-description-pattern:
+      subject: Tag
+      property: description
+      message: Tag description must be at least 13 characters and end with a full stop.
+      severity: error
+      minLength: 13
+      pattern: /\.$/
 #property example
-      - subject: PathItem
-        property: get
-        message: Every path item must have a GET operation.
-        defined: true
-      - subject: PathItem
-        message: Every path item must have a GET(required) operation.
-        required:
-          - get
-      - subject: Tag
-        property:
-          - name
-          - description
-        message: Every tag must have a name and description.
-        defined: true
+    assert/path-item-get-defined:
+      subject: PathItem
+      property: get
+      message: Every path item must have a GET operation.
+      defined: true
+    assert/path-item-get-required:
+      subject: PathItem
+      message: Every path item must have a GET(required) operation.
+      required:
+        - get
+    assert/tag-name-and-desc:
+      subject: Tag
+      property:
+        - name
+        - description
+      message: Every tag must have a name and description.
+      defined: true
 #context example
-      - subject:
-          - MediaTypeMap
-        context:
-          - type: Operation
-            excludeParentKeys:
-              - put
-          - type: Response
-            matchParentKeys: ['201', '200']
-        disallowed: [ 'application/pdf' ]
-        message: Media type should not be pdf
+    assert/operation-w-context:
+      subject:
+        - MediaTypeMap
+      context:
+        - type: Operation
+          excludeParentKeys:
+            - put
+        - type: Response
+          matchParentKeys: ['201', '200']
+      disallowed: [ 'application/pdf' ]
+      message: Media type should not be pdf
 #enum example
-      - subject: MediaTypeMap
-        message: Only application/pdf can be used
-        severity: error
-        enum:
-          - application/pdf
-      - subject: Operation
-        property: summary
-        message: Summary must be one of the predefined values
-        suggest:
-          - change to 'My resource'
-          - change to 'My collection'
-        severity: error
-        enum:
-          - My resource
-          - My collection
+    assert/media-type-pdf:
+      subject: MediaTypeMap
+      message: Only application/pdf can be used
+      severity: error
+      enum:
+        - application/pdf
+    assert/operation-summary-suggest:
+      subject: Operation
+      property: summary
+      message: Summary must be one of the predefined values
+      suggest:
+        - change to 'My resource'
+        - change to 'My collection'
+      severity: error
+      enum:
+        - My resource
+        - My collection
 #pattern example
-      - subject: Operation
-        property: summary
-        message: Summary should match a regex
-        severity: error
-        pattern: /resource/
+    assert/operation-summary-pattern:
+      subject: Operation
+      property: summary
+      message: Summary should match a regex
+      severity: error
+      pattern: /resource/
 #casing
-      - subject: Operation
-        property: operationId
-        message: NamedExamples key must be in PascalCase
-        severity: error
-        casing: camelCase
+    assert/operation-id-camel-case:
+      subject: Operation
+      property: operationId
+      message: NamedExamples key must be in PascalCase
+      severity: error
+      casing: camelCase
 #mutuallyExclusive example
-      - subject: Operation
-        message: "Operation must not define both properties together: description and externalDocs"
-        severity: error
-        mutuallyExclusive:
-          - description
-          - externalDocs
+    assert/operation-mutually-exclusive:
+      subject: Operation
+      message: "Operation must not define both properties together: description and externalDocs"
+      severity: error
+      mutuallyExclusive:
+        - description
+        - externalDocs
 #mutuallyRequired example
-      - subject: SchemaProperties
-        context:
-          - type: Response
-        message: The created_at and updated_at properties are mutually required
-        severity: error
-        mutuallyRequired:
-          - created_at
-          - updated_at
+    assert/operation-mutually-required:
+      subject: SchemaProperties
+      context:
+        - type: Response
+      message: The created_at and updated_at properties are mutually required
+      severity: error
+      mutuallyRequired:
+        - created_at
+        - updated_at
 #mutuallyRequired example with context
-      - subject: ResponsesMap
-        context:
-            - type: Operation
-              matchParentKeys:
-                - put
-        message: Must mutually define 200 and 201 responses for PUT requests.
-        severity: error
-        mutuallyRequired:
-          - '200'
-          - '201'
+    assert/operation-mutually-required-w-context:
+      subject: ResponsesMap
+      context:
+          - type: Operation
+            matchParentKeys:
+              - put
+      message: Must mutually define 200 and 201 responses for PUT requests.
+      severity: error
+      mutuallyRequired:
+        - '200'
+        - '201'
 #disallowed example
-      - subject: Operation
-        message: x-code-samples and x-internal must not be defined
-        severity: error
-        disallowed:
-          - x-code-samples
-          - x-internal
+    assert/operation-disallowed:
+      subject: Operation
+      message: x-code-samples and x-internal must not be defined
+      severity: error
+      disallowed:
+        - x-code-samples
+        - x-internal
 #defined example
-      - subject: Operation
-        property: x-codeSamples
-        message: x-codeSamples must be defined
-        severity: error
-        defined: true
+    assert/operation-x-code-samples-defined:
+      subject: Operation
+      property: x-codeSamples
+      message: x-codeSamples must be defined
+      severity: error
+      defined: true
 # undefined example
-      - subject: Operation
-        property: x-code-samples
-        message: x-code-samples is deprecated
-        suggest:
-          - x-codeSamples instead of x-code-samples
-        severity: error
-        undefined: true
+    assert/operation-x-code-samples-undefined:
+      subject: Operation
+      property: x-code-samples
+      message: x-code-samples is deprecated
+      suggest:
+        - x-codeSamples instead of x-code-samples
+      severity: error
+      undefined: true
 #nonEmpty example
-      - subject: Operation
-        property: summary
-        message: Operation summary should not be empty
-        severity: error
-        nonEmpty: true
+    assert/operation-summary-non-empty:
+      subject: Operation
+      property: summary
+      message: Operation summary should not be empty
+      severity: error
+      nonEmpty: true
 #minLength example
-      - subject: Operation
-        property: summary
-        message: Operation summary must have minimum of 2 chars length
-        severity: error
-        minLength: 2
+    assert/operation-summary-min-length:
+      subject: Operation
+      property: summary
+      message: Operation summary must have minimum of 2 chars length
+      severity: error
+      minLength: 2
 #maxLength example
-      - subject: Operation
-        property: summary
-        message: Operation summary must have a maximum of 2 characters
-        severity: error
-        maxLength: 2
+    assert/operation-summary-max-length:
+      subject: Operation
+      property: summary
+      message: Operation summary must have a maximum of 2 characters
+      severity: error
+      maxLength: 2
diff --git a/__tests__/lint/assertions-error/snapshot.js b/__tests__/lint/assertions-error/snapshot.js
index 7ec8294f..b5af92f3 100644
--- a/__tests__/lint/assertions-error/snapshot.js
+++ b/__tests__/lint/assertions-error/snapshot.js
@@ -14,7 +14,7 @@ Tag description must be at least 13 characters and end with a full stop.
 18 |   - name: about
 19 |     description: Everything about your Pets
 
-Error was generated by the assertion #1 rule.
+Error was generated by the tag-description-pattern assertion rule.
 
 
 [2] openapi.yaml:19:18 at #/tags/1/description
@@ -28,7 +28,7 @@ Tag description must be at least 13 characters and end with a full stop.
 20 |   - name: Without description
 21 | paths:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the tag-description-pattern assertion rule.
 
 
 [3] openapi.yaml:20:5 at #/tags/2/description
@@ -42,7 +42,7 @@ Every tag must have a name and description.
 21 | paths:
 22 |   /pet/findByStatus:
 
-Error was generated by the assertion #4 rule.
+Error was generated by the tag-name-and-desc assertion rule.
 
 
 [4] openapi.yaml:23:5 at #/paths/~1pet~1findByStatus/get
@@ -60,7 +60,7 @@ Every path item must have a GET operation.
    | ^
 83 |
 
-Error was generated by the assertion #2 rule.
+Error was generated by the path-item-get-defined assertion rule.
 
 
 [5] openapi.yaml:22:3 at #/paths/~1pet~1findByStatus
@@ -74,7 +74,7 @@ Every path item must have a GET(required) operation.
 23 |     put:
 24 |       operationId: exampleOne
 
-Error was generated by the assertion #3 rule.
+Error was generated by the path-item-get-required assertion rule.
 
 
 [6] openapi.yaml:23:5 at #/paths/~1pet~1findByStatus/put
@@ -88,7 +88,7 @@ Operation must not define both properties together: description and externalDocs
 24 |       operationId: exampleOne
 25 |       summary: My resource
 
-Error was generated by the assertion #10 rule.
+Error was generated by the operation-mutually-exclusive assertion rule.
 
 
 [7] openapi.yaml:24:7 at #/paths/~1pet~1findByStatus/put/x-codeSamples
@@ -107,7 +107,7 @@ x-codeSamples must be defined
 56 |   post:
 57 |     operationId: EXAMPLE
 
-Error was generated by the assertion #14 rule.
+Error was generated by the operation-x-code-samples-defined assertion rule.
 
 
 [8] openapi.yaml:25:16 at #/paths/~1pet~1findByStatus/put/summary
@@ -121,7 +121,7 @@ Operation summary must have a maximum of 2 characters
 26 |   description: summary example
 27 |   externalDocs:
 
-Error was generated by the assertion #18 rule.
+Error was generated by the operation-summary-max-length assertion rule.
 
 
 [9] openapi.yaml:31:9 at #/paths/~1pet~1findByStatus/put/requestBody/content
@@ -135,7 +135,7 @@ Only application/pdf can be used
 32 |     application/json:
 33 |       schema:
 
-Error was generated by the assertion #6 rule.
+Error was generated by the media-type-pdf assertion rule.
 
 
 [10] openapi.yaml:41:7 at #/paths/~1pet~1findByStatus/put/responses
@@ -149,7 +149,7 @@ Must mutually define 200 and 201 responses for PUT requests.
 42 |   '404':
 43 |     description: summary example
 
-Error was generated by the assertion #12 rule.
+Error was generated by the operation-mutually-required-w-context assertion rule.
 
 
 [11] openapi.yaml:50:17 at #/paths/~1pet~1findByStatus/put/responses/201/content/application~1pdf/schema/properties
@@ -163,7 +163,7 @@ The created_at and updated_at properties are mutually required
 51 |     created_at:
 52 |       type: string
 
-Error was generated by the assertion #11 rule.
+Error was generated by the operation-mutually-required assertion rule.
 
 
 [12] openapi.yaml:58:16 at #/paths/~1pet~1findByStatus/post/summary
@@ -181,7 +181,7 @@ Did you mean:
 59 |   responses:
 60 |     '201':
 
-Error was generated by the assertion #7 rule.
+Error was generated by the operation-summary-suggest assertion rule.
 
 
 [13] openapi.yaml:58:16 at #/paths/~1pet~1findByStatus/post/summary
@@ -195,7 +195,7 @@ Summary should match a regex
 59 |   responses:
 60 |     '201':
 
-Error was generated by the assertion #8 rule.
+Error was generated by the operation-summary-pattern assertion rule.
 
 
 [14] openapi.yaml:57:20 at #/paths/~1pet~1findByStatus/post/operationId
@@ -209,7 +209,7 @@ NamedExamples key must be in PascalCase
 58 |   summary: ''
 59 |   responses:
 
-Error was generated by the assertion #9 rule.
+Error was generated by the operation-id-camel-case assertion rule.
 
 
 [15] openapi.yaml:56:5 at #/paths/~1pet~1findByStatus/post
@@ -223,7 +223,7 @@ x-code-samples and x-internal must not be defined
 57 |   operationId: EXAMPLE
 58 |   summary: ''
 
-Error was generated by the assertion #13 rule.
+Error was generated by the operation-disallowed assertion rule.
 
 
 [16] openapi.yaml:57:7 at #/paths/~1pet~1findByStatus/post/x-codeSamples
@@ -241,7 +241,7 @@ x-codeSamples must be defined
    | ^
 83 |
 
-Error was generated by the assertion #14 rule.
+Error was generated by the operation-x-code-samples-defined assertion rule.
 
 
 [17] openapi.yaml:79:9 at #/paths/~1pet~1findByStatus/post/x-code-samples
@@ -262,7 +262,7 @@ Did you mean: x-codeSamples instead of x-code-samples ?
    |       ^
 83 |
 
-Error was generated by the assertion #15 rule.
+Error was generated by the operation-x-code-samples-undefined assertion rule.
 
 
 [18] openapi.yaml:58:16 at #/paths/~1pet~1findByStatus/post/summary
@@ -276,7 +276,7 @@ Operation summary should not be empty
 59 |   responses:
 60 |     '201':
 
-Error was generated by the assertion #16 rule.
+Error was generated by the operation-summary-non-empty assertion rule.
 
 
 [19] openapi.yaml:58:16 at #/paths/~1pet~1findByStatus/post/summary
@@ -290,7 +290,7 @@ Operation summary must have minimum of 2 chars length
 59 |   responses:
 60 |     '201':
 
-Error was generated by the assertion #17 rule.
+Error was generated by the operation-summary-min-length assertion rule.
 
 
 [20] openapi.yaml:58:7 at #/paths/~1pet~1findByStatus/post/summary
@@ -318,7 +318,7 @@ Media type should not be pdf
 63 |     application/pdf:
 64 |       schema:
 
-Error was generated by the assertion #5 rule.
+Error was generated by the operation-w-context assertion rule.
 
 
 [22] openapi.yaml:20:5 at #/tags/2/description
diff --git a/__tests__/lint/assertions-max-length-array-error/.redocly.yaml b/__tests__/lint/assertions-max-length-array-error/.redocly.yaml
index e7a30827..633c22bd 100644
--- a/__tests__/lint/assertions-max-length-array-error/.redocly.yaml
+++ b/__tests__/lint/assertions-max-length-array-error/.redocly.yaml
@@ -4,9 +4,9 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: tags
-        message: Operation tags should have maximum 2 items
-        maxLength: 2
+    assert/tags-max-length:
+      subject: Operation
+      property: tags
+      message: Operation tags should have maximum 2 items
+      maxLength: 2
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-max-length-array-error/snapshot.js b/__tests__/lint/assertions-max-length-array-error/snapshot.js
index aaeebc2b..b3ac101a 100644
--- a/__tests__/lint/assertions-max-length-array-error/snapshot.js
+++ b/__tests__/lint/assertions-max-length-array-error/snapshot.js
@@ -20,7 +20,7 @@ Operation tags should have maximum 2 items
 26 |   '200':
 27 |     description: example description
 
-Error was generated by the assertion #1 rule.
+Error was generated by the tags-max-length assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-max-length-string-error/.redocly.yaml b/__tests__/lint/assertions-max-length-string-error/.redocly.yaml
index 0b7db792..36aa74f6 100644
--- a/__tests__/lint/assertions-max-length-string-error/.redocly.yaml
+++ b/__tests__/lint/assertions-max-length-string-error/.redocly.yaml
@@ -4,9 +4,9 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject: Info
-        property: description
-        message: Info description should be maximum 10 chars length
-        maxLength: 10
+    assert/description-max-length:
+      subject: Info
+      property: description
+      message: Info description should be maximum 10 chars length
+      maxLength: 10
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-max-length-string-error/snapshot.js b/__tests__/lint/assertions-max-length-string-error/snapshot.js
index fd3f83f3..fb179c1f 100644
--- a/__tests__/lint/assertions-max-length-string-error/snapshot.js
+++ b/__tests__/lint/assertions-max-length-string-error/snapshot.js
@@ -14,7 +14,7 @@ Info description should be maximum 10 chars length
 10 | version: '1.0'
 11 | contact:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the description-max-length assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-min-length-array-error/.redocly.yaml b/__tests__/lint/assertions-min-length-array-error/.redocly.yaml
index defc1955..ce092f08 100644
--- a/__tests__/lint/assertions-min-length-array-error/.redocly.yaml
+++ b/__tests__/lint/assertions-min-length-array-error/.redocly.yaml
@@ -4,9 +4,9 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: tags
-        message: Operation tags should have minimum 4 items
-        minLength: 4
+    assert/tags-min-length:
+      subject: Operation
+      property: tags
+      message: Operation tags should have minimum 4 items
+      minLength: 4
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-min-length-array-error/snapshot.js b/__tests__/lint/assertions-min-length-array-error/snapshot.js
index 0ee937eb..95b87d92 100644
--- a/__tests__/lint/assertions-min-length-array-error/snapshot.js
+++ b/__tests__/lint/assertions-min-length-array-error/snapshot.js
@@ -20,7 +20,7 @@ Operation tags should have minimum 4 items
 26 |   '200':
 27 |     description: example description
 
-Error was generated by the assertion #1 rule.
+Error was generated by the tags-min-length assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-min-length-string-error/.redocly.yaml b/__tests__/lint/assertions-min-length-string-error/.redocly.yaml
index 6696963e..56516791 100644
--- a/__tests__/lint/assertions-min-length-string-error/.redocly.yaml
+++ b/__tests__/lint/assertions-min-length-string-error/.redocly.yaml
@@ -4,9 +4,9 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject: Info
-        property: description
-        message: Info description should be minimum 20 chars length
-        minLength: 20
+    assert/description-min-length:
+      subject: Info
+      property: description
+      message: Info description should be minimum 20 chars length
+      minLength: 20
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-min-length-string-error/snapshot.js b/__tests__/lint/assertions-min-length-string-error/snapshot.js
index 868fd90d..7297cec4 100644
--- a/__tests__/lint/assertions-min-length-string-error/snapshot.js
+++ b/__tests__/lint/assertions-min-length-string-error/snapshot.js
@@ -14,7 +14,7 @@ Info description should be minimum 20 chars length
 10 | version: '1.0'
 11 | contact:
 
-Error was generated by the assertion #1 rule.
+Error was generated by the description-min-length assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-mutually-exclusive-error/.redocly.yaml b/__tests__/lint/assertions-mutually-exclusive-error/.redocly.yaml
index b90097b5..799cb0f7 100644
--- a/__tests__/lint/assertions-mutually-exclusive-error/.redocly.yaml
+++ b/__tests__/lint/assertions-mutually-exclusive-error/.redocly.yaml
@@ -4,12 +4,12 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-          - type: PathItem
-        subject: Operation
-        message: Operation should not have summary and tags fields simultaneously
-        mutuallyExclusive:
-          - summary
-          - tags
+    assert/path-item-mutually-exclusive:
+      context:
+        - type: PathItem
+      subject: Operation
+      message: Operation should not have summary and tags fields simultaneously
+      mutuallyExclusive:
+        - summary
+        - tags
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-mutually-exclusive-error/snapshot.js b/__tests__/lint/assertions-mutually-exclusive-error/snapshot.js
index f708452b..91ebc9aa 100644
--- a/__tests__/lint/assertions-mutually-exclusive-error/snapshot.js
+++ b/__tests__/lint/assertions-mutually-exclusive-error/snapshot.js
@@ -14,7 +14,7 @@ Operation should not have summary and tags fields simultaneously
 19 |       operationId: example
 20 |       summary: summary example
 
-Error was generated by the assertion #1 rule.
+Error was generated by the path-item-mutually-exclusive assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-mutually-required-error/.redocly.yaml b/__tests__/lint/assertions-mutually-required-error/.redocly.yaml
index 51e4e947..5f3b5711 100644
--- a/__tests__/lint/assertions-mutually-required-error/.redocly.yaml
+++ b/__tests__/lint/assertions-mutually-required-error/.redocly.yaml
@@ -4,12 +4,12 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-        - type: PathItem
-        subject: Operation
-        message: Operation should have summary and security fields simultaneously
-        mutuallyRequired:
-          - summary
-          - security
+    assert/path-item-mutually-required:
+      context:
+      - type: PathItem
+      subject: Operation
+      message: Operation should have summary and security fields simultaneously
+      mutuallyRequired:
+        - summary
+        - security
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-mutually-required-error/snapshot.js b/__tests__/lint/assertions-mutually-required-error/snapshot.js
index 5104dfe3..8a9b8f00 100644
--- a/__tests__/lint/assertions-mutually-required-error/snapshot.js
+++ b/__tests__/lint/assertions-mutually-required-error/snapshot.js
@@ -14,7 +14,7 @@ Operation should have summary and security fields simultaneously
 19 |       operationId: example
 20 |       summary: summary example
 
-Error was generated by the assertion #1 rule.
+Error was generated by the path-item-mutually-required assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-non-empty-error/.redocly.yaml b/__tests__/lint/assertions-non-empty-error/.redocly.yaml
index c30aca2d..b6b28c3b 100644
--- a/__tests__/lint/assertions-non-empty-error/.redocly.yaml
+++ b/__tests__/lint/assertions-non-empty-error/.redocly.yaml
@@ -4,9 +4,9 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Operation summary should not be empty
-        nonEmpty: true
+    assert/summary-non-empty:
+      subject: Operation
+      property: summary
+      message: Operation summary should not be empty
+      nonEmpty: true
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-non-empty-error/snapshot.js b/__tests__/lint/assertions-non-empty-error/snapshot.js
index 7582af04..3f4e355b 100644
--- a/__tests__/lint/assertions-non-empty-error/snapshot.js
+++ b/__tests__/lint/assertions-non-empty-error/snapshot.js
@@ -14,7 +14,7 @@ Operation summary should not be empty
 21 |   tags:
 22 |     - foo
 
-Error was generated by the assertion #1 rule.
+Error was generated by the summary-non-empty assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-pattern-error/.redocly.yaml b/__tests__/lint/assertions-pattern-error/.redocly.yaml
index cc9f2f12..7531986a 100644
--- a/__tests__/lint/assertions-pattern-error/.redocly.yaml
+++ b/__tests__/lint/assertions-pattern-error/.redocly.yaml
@@ -4,9 +4,9 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Operation summary should have 'test' word
-        pattern: '/test/'
+    assert/summary-pattern:
+      subject: Operation
+      property: summary
+      message: Operation summary should have 'test' word
+      pattern: '/test/'
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-pattern-error/snapshot.js b/__tests__/lint/assertions-pattern-error/snapshot.js
index 322b6b0f..515db645 100644
--- a/__tests__/lint/assertions-pattern-error/snapshot.js
+++ b/__tests__/lint/assertions-pattern-error/snapshot.js
@@ -14,7 +14,7 @@ Operation summary should have 'test' word
 21 |   tags:
 22 |     - foo
 
-Error was generated by the assertion #1 rule.
+Error was generated by the summary-pattern assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-show-suggestions/.redocly.yaml b/__tests__/lint/assertions-show-suggestions/.redocly.yaml
index 80b1e018..c4c116b2 100644
--- a/__tests__/lint/assertions-show-suggestions/.redocly.yaml
+++ b/__tests__/lint/assertions-show-suggestions/.redocly.yaml
@@ -4,14 +4,14 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - context:
-        - type: PathItem
-        subject: Operation
-        property: summary
-        message: Operation summary should have at least 10 chars length
-        suggest:
-          - Suggestion 1
-          - Suggestion 2
-        minLength: 20
+    assert/path-item-suggest:
+      context:
+      - type: PathItem
+      subject: Operation
+      property: summary
+      message: Operation summary should have at least 10 chars length
+      suggest:
+        - Suggestion 1
+        - Suggestion 2
+      minLength: 20
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-show-suggestions/snapshot.js b/__tests__/lint/assertions-show-suggestions/snapshot.js
index d059b90f..6da30ee3 100644
--- a/__tests__/lint/assertions-show-suggestions/snapshot.js
+++ b/__tests__/lint/assertions-show-suggestions/snapshot.js
@@ -18,7 +18,7 @@ Did you mean:
 21 |   tags:
 22 |     - foo
 
-Error was generated by the assertion #1 rule.
+Error was generated by the path-item-suggest assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions-undefined-error/.redocly.yaml b/__tests__/lint/assertions-undefined-error/.redocly.yaml
index a6fa8193..1453a5c5 100644
--- a/__tests__/lint/assertions-undefined-error/.redocly.yaml
+++ b/__tests__/lint/assertions-undefined-error/.redocly.yaml
@@ -4,9 +4,9 @@ apis:
 
 lint:
   rules:
-    assertions:
-      - subject: Operation
-        property: summary
-        message: Operation summary should be undefined
-        undefined: true
+    assert/summary-undefined:
+      subject: Operation
+      property: summary
+      message: Operation summary should be undefined
+      undefined: true
   extends: []
\ No newline at end of file
diff --git a/__tests__/lint/assertions-undefined-error/snapshot.js b/__tests__/lint/assertions-undefined-error/snapshot.js
index 7b27106b..4001d787 100644
--- a/__tests__/lint/assertions-undefined-error/snapshot.js
+++ b/__tests__/lint/assertions-undefined-error/snapshot.js
@@ -14,7 +14,7 @@ Operation summary should be undefined
 21 |   tags:
 22 |     - foo
 
-Error was generated by the assertion #1 rule.
+Error was generated by the summary-undefined assertion rule.
 
 
 /openapi.yaml: validated in <test>ms
diff --git a/__tests__/lint/assertions/.redocly.yaml b/__tests__/lint/assertions/.redocly.yaml
index fa073096..ca89b6ce 100644
--- a/__tests__/lint/assertions/.redocly.yaml
+++ b/__tests__/lint/assertions/.redocly.yaml
@@ -5,131 +5,148 @@ apis:
 lint:
   extends: []
   rules:
-    assertions:
-      - subject: Tag
-        property: description
-        message: Tag description must be at least 13 characters and end with a full stop.
-        severity: error
-        minLength: 13
-        pattern: /\.$/
+    assert/tag-description:
+      subject: Tag
+      property: description
+      message: Tag description must be at least 13 characters and end with a full stop.
+      severity: error
+      minLength: 13
+      pattern: /\.$/
 #property example
-      - subject: PathItem
-        property: get
-        message: Every path item must have a GET operation.
-        defined: true
-      - subject: PathItem
-        message: Every path item must have a GET operation.
-        required:
-          - get
-      - subject: Tag
-        property:
-          - name
-          - description
-        message: Every tag must have a name and description.
-        defined: true
+    assert/path-item-get-defined:
+      subject: PathItem
+      property: get
+      message: Every path item must have a GET operation.
+      defined: true
+    assert/path-item-get-required:
+      subject: PathItem
+      message: Every path item must have a GET operation.
+      required:
+        - get
+    assert/tag-name-and-desc:
+      subject: Tag
+      property:
+        - name
+        - description
+      message: Every tag must have a name and description.
+      defined: true
 #context example
-      - subject:
-          - MediaTypeMap
-        context:
-          - type: Operation
-            excludeParentKeys:
-              - put
-          - type: Response
-            matchParentKeys: ['201', '200']
-        disallowed: [ 'application/pdf' ]
-        message: Media type should not be pdf
+    assert/media-type-map-not-pdf:
+      subject:
+        - MediaTypeMap
+      context:
+        - type: Operation
+          excludeParentKeys:
+            - put
+        - type: Response
+          matchParentKeys: ['201', '200']
+      disallowed: [ 'application/pdf' ]
+      message: Media type should not be pdf
 #enum example
-      - subject: MediaTypeMap
-        message: Only application/json and application/pdf can be used
-        severity: error
-        enum:
-          - application/json
-          - application/pdf
-      - subject: Operation
-        property: summary
-        message: Summary must be one of the predefined values
-        suggest:
-          - change to 'My resource'
-          - change to 'My collection'
-        severity: error
-        enum:
-          - My resource
-          - My collection
+    assert/media-type-map-enum:
+      subject: MediaTypeMap
+      message: Only application/json and application/pdf can be used
+      severity: error
+      enum:
+        - application/json
+        - application/pdf
+    assert/operation-summary-suggest:
+      subject: Operation
+      property: summary
+      message: Summary must be one of the predefined values
+      suggest:
+        - change to 'My resource'
+        - change to 'My collection'
+      severity: error
+      enum:
+        - My resource
+        - My collection
 #pattern example
-      - subject: Operation
-        property: summary
-        message: Summary should match a regex
-        severity: error
-        pattern: /resource/
+    assert/operation-summary-pattern:
+      subject: Operation
+      property: summary
+      message: Summary should match a regex
+      severity: error
+      pattern: /resource/
 #casing
-      - subject: Operation
-        property: operationId
-        message: NamedExamples key must be in PascalCase
-        severity: error
-        casing: camelCase
+    assert/operation-id-casing:
+      subject: Operation
+      property: operationId
+      message: NamedExamples key must be in PascalCase
+      severity: error
+      casing: camelCase
 #mutuallyExclusive example
-      - subject: Operation
-        message: "Operation must not define both properties together: description and externalDocs"
-        severity: error
-        mutuallyExclusive:
-          - description
-          - externalDocs
+    assert/operation-mutually-exclusive:
+      subject: Operation
+      message: "Operation must not define both properties together: description and externalDocs"
+      severity: error
+      mutuallyExclusive:
+        - description
+        - externalDocs
 #mutuallyRequired example
-      - subject: SchemaProperties
-        context:
-          - type: Response
-        message: The created_at and updated_at properties are mutually required
-        severity: error
-        mutuallyRequired:
-          - created_at
-          - updated_at
+    assert/schema-properties-mutually-required:
+      subject: SchemaProperties
+      context:
+        - type: Response
+      message: The created_at and updated_at properties are mutually required
+      severity: error
+      mutuallyRequired:
+        - created_at
+        - updated_at
 #mutuallyRequired example with context
-      - subject: ResponsesMap
-        context:
-            - type: Operation
-              matchParentKeys:
-                - put
-        message: Must mutually define 200 and 201 responses for PUT requests.
-        severity: error
-        mutuallyRequired:
-          - '200'
-          - '201'
+    assert/response-map-required-with-context:
+      subject: ResponsesMap
+      context:
+          - type: Operation
+            matchParentKeys:
+              - put
+      message: Must mutually define 200 and 201 responses for PUT requests.
+      severity: error
+      mutuallyRequired:
+        - '200'
+        - '201'
 #disallowed example
-      - subject: Operation
-        message: x-code-samples and x-internal must not be defined
-        severity: error
-        disallowed:
-          - x-code-samples
-          - x-internal
+    assert/operation-disallowed:
+      subject: Operation
+      message: x-code-samples and x-internal must not be defined
+      severity: error
+      disallowed:
+        - x-code-samples
+        - x-internal
 #defined example
-      - subject: Operation
-        property: x-codeSamples
-        message: x-codeSamples must be defined
-        severity: error
-        defined: true
+    assert/operation-defined:
+      subject: Operation
+      property: x-codeSamples
+      message: x-codeSamples must be defined
+      severity: error
+      defined: true
 # undefined example
-      - subject: Operation
-        property: x-code-samples
-        message: x-code-samples is deprecated
-        suggest:
-          - x-codeSamples instead of x-code-samples
-        severity: error
-        undefined: true
+    assert/operation-undefined:
+      subject: Operation
+      property: x-code-samples
+      message: x-code-samples is deprecated
+      suggest:
+        - x-codeSamples instead of x-code-samples
+      severity: error
+      undefined: true
 #nonEmpty example
-      - subject: Operation
-        property: summary
-        message: Operation summary should not be empty
-        severity: error
-        nonEmpty: true
+    assert/operation-non-empty:
+      subject: Operation
+      property: summary
+      message: Operation summary should not be empty
+      severity: error
+      nonEmpty: true
 #minLength example
-      - subject: Operation
-        property: summary
-        message: Operation summary must have minimum of 2 chars length
-        severity: error
-        minLength: 2
+    assert/operation-min-length:
+      subject: Operation
+      property: summary
+      message: Operation summary must have minimum of 2 chars length
+      severity: error
+      minLength: 2
 #maxLength example
-      - subject: Operation
-        property: summary
-        message: Operation summary must have a maximum of 2 characters
-        severity: error
-        maxLength: 20
+    assert/operation-max-length:
+      subject: Operation
+      property: summary
+      message: Operation summary must have a maximum of 2 characters
+      severity: error
+      maxLength: 20
diff --git a/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap b/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap
index ec503b85..e8cfabea 100644
--- a/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap
+++ b/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap
@@ -103,7 +103,24 @@ Object {
   "preprocessors": Object {},
   "recommendedFallback": undefined,
   "rules": Object {
-    "assertions": "warn",
+    "assertions": Array [
+      Object {
+        "assertionId": "path-item-get-defined",
+        "defined": true,
+        "message": "Every path item must have a GET operation.",
+        "property": "get",
+        "subject": "PathItem",
+      },
+      Object {
+        "assertionId": "tag-description",
+        "message": "Tag description must be at least 13 characters and end with a full stop.",
+        "minLength": 13,
+        "pattern": "/\\\\.$/",
+        "property": "description",
+        "severity": "error",
+        "subject": "Tag",
+      },
+    ],
     "boolean-parameter-prefixes": "error",
     "info-contact": "off",
     "info-description": "warn",
diff --git a/packages/core/src/config/__tests__/config-resolvers.test.ts b/packages/core/src/config/__tests__/config-resolvers.test.ts
index 4dce0ef6..6f0f7df4 100644
--- a/packages/core/src/config/__tests__/config-resolvers.test.ts
+++ b/packages/core/src/config/__tests__/config-resolvers.test.ts
@@ -130,6 +130,37 @@ describe('resolveLint', () => {
     expect(lint).toMatchSnapshot();
   });
 
+  it('should correctly merge assertions from nested config', async () => {
+    const lintConfig = {
+      extends: ['local-config-with-file.yaml'],
+    };
+
+    const lint = await resolveLint({
+      lintConfig,
+      configPath,
+    });
+
+    expect(Array.isArray(lint.rules?.assertions)).toEqual(true);
+    expect(lint.rules?.assertions).toMatchObject( [
+      {
+        subject: 'PathItem',
+        property: 'get',
+        message: 'Every path item must have a GET operation.',
+        defined: true,
+        assertionId: 'path-item-get-defined'
+      },
+      {
+        subject: 'Tag',
+        property: 'description',
+        message: 'Tag description must be at least 13 characters and end with a full stop.',
+        severity: 'error',
+        minLength: 13,
+        pattern: '/\\.$/',
+        assertionId: 'tag-description'
+      }
+    ])
+  });
+
   it('should resolve extends with url file config witch contains path to nested config', async () => {
     const lintConfig = {
       // This points to ./fixtures/resolve-remote-configs/remote-config.yaml
diff --git a/packages/core/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml b/packages/core/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml
index 45fda2fe..6c1d250d 100644
--- a/packages/core/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml
+++ b/packages/core/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml
@@ -3,5 +3,10 @@ lint:
     - plugin.js
   rules:
     operation-2xx-response: error
+    assert/path-item-get-defined:
+      subject: PathItem
+      property: get
+      message: Every path item must have a GET operation.
+      defined: true
   extends:
     - test-plugin-nested/all
diff --git a/packages/core/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml b/packages/core/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml
index 8de7ceb2..179ff990 100644
--- a/packages/core/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml
+++ b/packages/core/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml
@@ -2,6 +2,13 @@ lint:
   rules:
     no-invalid-media-type-examples: warn
     operation-4xx-response: off
+    assert/tag-description:
+      subject: Tag
+      property: description
+      message: Tag description must be at least 13 characters and end with a full stop.
+      severity: error
+      minLength: 13
+      pattern: /\.$/
   plugins:
     - plugin.js
     - api/plugin.js