← RedoclyCONTENT HISTORY

Update to Redocly

Snapshot Nov 2, 2026 · 18:12 UTC · version 0.1.5

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save Redocly and check future changes in Following.

Up to 10 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Implement a Redocly CLI lint rule in a user's redocly.yaml or custom plugin for an OpenAPI, AsyncAPI, or Arazzo description. Use when the user wants a convention enforced, asks for a configurable rule or a plugin rule, debugs a rule that never reports or gives false positives, or migrates rules from another linter such as Spectral.",
  "included_files": [],
  "name": "redocly-lint-rules",
  "skill_md_contents": "---\nname: redocly-lint-rules\ndescription: Implement a Redocly CLI lint rule in a user's redocly.yaml or custom plugin for an OpenAPI, AsyncAPI, or Arazzo description. Use when the user wants a convention enforced, asks for a configurable rule or a plugin rule, debugs a rule that never reports or gives false positives, or migrates rules from another linter such as Spectral.\n---\n\n# Implementing a Redocly CLI lint rule\n\nTurn a check written in plain language (\"every array schema must declare `items`\") into a Redocly lint rule that works and is tested.\n\n**The ladder.** Redocly gives three ways to do a check. The first is the cheapest to maintain.\n\n1. **Built-in rule** — one of the [built-in rules](https://redocly.com/docs/cli/rules/built-in-rules) does the check.\n2. **Configurable rule** — one or more `rule/*` entries in `redocly.yaml` do the check with assertions.\n3. **Custom plugin** — JavaScript code that implements your own rule with the visitor pattern. Use this rung last.\n\nDo the checks one at a time.\nStart each check at rung 1.\nGo down one rung only when the current rung cannot do the check.\nTake all syntax from the [official docs](https://redocly.com/docs/cli/) or the [cookbook](https://github.com/Redocly/redocly-cli/tree/main/cookbook).\nTrust the docs, not your memory.\nUse modern syntax, such as ESM instead of CommonJS. If the project has its own style, follow the project.\n\n## 1. Triage\n\nFor each check, write down three things: the node it looks at, what must be true, and what a violation looks like.\nTriage each check on its own. Different checks can land on different rungs.\n\nGet node names from the `inspect-node-types` command, not from memory.\nThe type tree is different in each specification and each version.\nThe names it prints are the `subject.type` of a configurable rule, and the visitor keys of a plugin.\n\n| to learn                              | run                             |\n| ------------------------------------- | ------------------------------- |\n| the type at one location              | `--pointer='#/paths/~1orders'`  |\n| every node, or every node of one type | no flag, or `--type=Schema`     |\n| which types the description uses      | `--summary`                     |\n| **where a type can sit**              | `--type=<Type> --parents`       |\n| the ancestors of one node             | `--pointer=<pointer> --parents` |\n\nThe last two rows give a rule its scope.\nWhen a type sits below more than one parent, the rule needs a `where` gate.\nWithout that gate, the rule also reports the branches you do not want.\nThe command shows the description in front of you. It does not show everything the specification permits.\nWhen the command does not print a branch, that branch is unproven. It is not absent.\nThe command does not shorten step 3. It gives you type names. It does not tell you whether your assertions express the check.\n\nWalk the ladder for each check:\n\n- Search the [built-in rules list](https://redocly.com/docs/cli/rules/built-in-rules) for the concept.\n  A trailing slash in a path gives `no-path-trailing-slash`.\n  When the config already extends a ruleset that turns the built-in on, tell the user. Do not add the rule again.\n  **Use the built-in when its description matches the check, even when it reports more than the rule you replace.**\n  A built-in is usually a better version of the same idea, so its extra findings are the check done correctly.\n  When you write the narrower behavior by hand, you exchange a maintained rule for code that you must maintain.\n  Go down one rung only when the built-in checks something different.\n  Report which built-in you took, and what it adds.\n- Make sure the built-in supports the spec versions that the project lints.\n  Some built-ins support one version only.\n  `no-invalid-media-type-examples` supports OpenAPI 3.x, so it leaves a Swagger 2.0 check unenforced.\n  Redocly prints `Unused rules found in redocly.yaml` when an enabled rule cannot apply to a document.\n  Correct that line. It is not noise.\n- Try assertions and a `where` gate. See \"Configurable rules\" below.\n  One check can need several `rule/*` entries, one for each case or location. That is still better than a plugin.\n- Write a plugin only when configurable rules cannot express the check, also in combination.\n  First work through the three dials below.\n  A plugin can do any check, but it is code. It adds complexity, maintenance, and its own bugs.\n- Tell the user to [file an issue](https://github.com/Redocly/redocly-cli/issues) only when no configurable rule can close a real gap.\n\nTriage is complete when each check has a rung.\nGive one solution for each check: the highest rung that works.\nReport only what the user must act on: a mismatch between a description and an implementation, an unproven rule, or a decision that could go another way.\nReport in the chat. Never write a summary file.\nThe config records the rest, so do not narrate the rung of each check.\n\n### Migrate from another linter\n\n- Migrate the **effective** ruleset, not only the rules written in the file.\n  An `extends`, or an inherited base, turns on more rules at their default severities. Each of those rules is also a check.\n  Resolve that base and count the rules before you start. Then you know the true size of the job.\n- Take each check from two places: the description of the rule, and its implementation.\n  Never take it from your memory of the rule name.\n  When the two disagree, use the description. The description says what the author wanted, and the implementation can hold a bug.\n  Tell the user about the difference.\n- Skip a rule with severity `off`. Do not migrate it, not even as a disabled entry.\n  When the enabled rules are done, list each skipped rule on one line. Then the user can decide.\n- For Spectral, read the map table in the [migration guide](https://redocly.com/docs/cli/guides/migrate-from-spectral) first.\n  Many Spectral core rules map to one built-in. An empty cell means that you go down the ladder.\n- When you copy a function from the source, you also copy its bugs into a stricter host.\n  Redocly stops the whole document when a rule throws, but the source linter can stop one rule only.\n  An unguarded match, such as `v.match(/…/)[0]` on a value that can fail to match, removes every finding in that file.\n  Add a guard to the code you copy. Then lint a document that holds the difficult shape.\n\n## 2. Configure\n\nWrite to the user's `redocly.yaml`. Use that exact name, with no leading dot.\nWhen the file exists, read it first, and keep the existing `extends` and rules.\nSet each rule to `error`, unless the user asks for another level.\nWhen you migrate, keep the source severity and choose the nearest Redocly level.\nOnly `error`, which fails the run, `warn`, and `off` exist, so `info` and `hint` become `warn`.\nReference every file you create from the config, and exercise it in the lint run. Leave no dead files.\n\n**A config with no `extends` inherits nothing** — not `recommended`, and not even `struct`.\nThe default base applies only when no config file exists.\nSo a `redocly.yaml` runs exactly the rules that it names.\nThe built-in rung exists only when `extends` names a ruleset, or when the config lists each built-in itself.\nWith neither, a document that breaks the specification three times lints clean.\n\n**Wire every rule.**\nA plugin in `plugins:` does nothing when its rules are absent from `rules:`, and Redocly gives no warning.\nAfter you write the config, list the rule ids that must be active.\nConfirm that each id is in `rules:` or in a spec-scoped block.\n\nWhen all rules are in, compare your list with the predefined [rulesets](https://redocly.com/docs/cli/rules): `minimal`, `recommended`, `recommended-strict`, and `spec`.\n\n- When your rules are close to one ruleset, extend it.\n  Keep only the differences as overrides: other severities, rules to turn off, and checks that the ruleset does not hold.\n- A ruleset also turns on rules that the user did not ask for.\n  Read what it adds, and override each rule that contradicts or repeats a rule you wrote.\n  A ruleset that turns on a check which the source sets to `off` is an error to correct, not a benefit.\n- When the overrides are as many as the rules, or more, keep the plain list and no `extends`.\n\n## 3. Verify\n\nA rule is **unproven** until a live run makes it report.\nProve each rule with `npx redocly lint <api-file>` in two directions:\n\n- a **violating** fixture: the rule reports, at the correct location;\n- a **conforming** fixture: the run stays silent.\n\nA false positive is the common failure, so a rule that passed only the violating fixture is unproven.\nWrite the violating fixture to separate this rule from the rule next to it: the rule you replace, or its neighbour in the config.\nTwo errors need that separation:\n\n- **Put a falsy value in the violating fixture.**\n  A rule that must reject an empty `description` must report on `description: ''`.\n  When you put `''` in the conforming fixture only, you prove nothing, because a presence assertion accepts it.\n  The rule then looks correct and the gap stays.\n- **A built-in that replaces two source checks needs a fixture that breaks only the second check.**\n  `no-unsafe-markdown` reports `<script>`, but never `eval(`.\n  A fixture that holds both gives the built-in credit for a check it does not do.\n\nTo keep the output clean, pass a small temporary config with `--config`.\nDelete that config and the fixtures when you finish.\nEvery snippet you deliver must be correct as written: it parses as YAML, and it behaves as you claim.\n\n**You are done when each check names a violating fixture that reported, and a conforming fixture that stayed silent.**\nName those fixtures in your report.\nA count is not evidence.\nA clean run on the project's own files is not evidence either: it shows only that nothing reports there, and that is true for a correct rule and for a dead rule.\nWhen you verify the risky rules and assume the others, the delivery is unproven. Say so, and name each unproven rule.\nA conforming fixture proves only the shapes inside it.\nA spec version you did not try, or a location that `--parents` showed and you did not exercise, stays untested. Report that.\nWhen a live run is impossible, mark the rule unverified. Do not claim that it works.\n\nYou are also done when each requested check is in the config or on the skipped list, and the project holds only the config and plugin files that you created.\n\n## Configurable rules\n\nDocs: [configurable rules](https://redocly.com/docs/cli/rules/configurable-rules).\nWorked examples: [cookbook/configurable-rules](https://github.com/Redocly/redocly-cli/tree/main/cookbook/configurable-rules).\n\nA rule is an entry under `rules:` with a name that starts with `rule/`:\n\n```yaml\nrules:\n  rule/my-check:\n    subject:\n      type: Schema # a node type from the docs' type list\n      property: title # optional\n    assertions:\n      defined: true\n    where: [] # optional gate\n    message: Custom problem text.\n    severity: error\n```\n\nPlain `rules` applies to every document that the project lints. Keep rules there by default.\nMove a rule under a spec-scoped key only when the check needs something that exists in one spec version, such as `nullable` in OAS 3.0, or when the user asks for the scoping.\nThe keys are `oas2Rules`, `oas3_0Rules`, `oas3_1Rules`, `oas3_2Rules`, `async2Rules`, `async3Rules`, `arazzo1Rules`, and `overlay1Rules`.\nA rule inside one key does not run for the other versions, so a check that the project needs on two versions needs an entry that both versions reach.\nScope a plugin rule inside the plugin instead, in its `rules` map.\n\n### What an assertion tests\n\nTwo facts cause most false positives and most silent misses.\n\n**An absent value passes.**\nNearly every assertion holds when the value it checks is absent.\n`const: array` on `property: type` matches a schema typed `array`, and also every schema with no `type`.\nSo when a `where` entry tests a property value, add `defined: true` beside the value assertion if the property can be absent.\n\n**Presence is not value.**\n`defined`, `required`, `requireAny`, `disallowed`, and `mutuallyExclusive` test whether a key exists.\nThey say nothing about the content, so `defined: true` on `description` passes for `description: ''`.\nAssert the value instead: `nonEmpty: true` rejects an empty string, and `const`, `enum`, and `pattern` test the content.\nThis is the most frequent defect in practice.\nIt looks correct in review, and only a violating fixture that holds `''` shows it.\n\nTwo results of these facts:\n\n- `const: <value>` is also the way to say \"this field must be falsy\", because the assertion holds for that value and for an absent field.\n- `requireAny` counts keys only.\n  \"At least one of these fields, and not empty\" has no assertion form. That check is a real plugin case.\n\n### What the assertion inspects\n\n- `property: name` — the value of that field on the subject node.\n- `property: [name, url, email]` — each listed field on its own.\n  Every field that fails becomes its own problem with its own JSON pointer, such as `#/info/contact/name`.\n- `property` absent — the **keys** of the subject node.\n  For example, `subject: { type: Paths }` with `assertions: { notPattern: /.+\\/$/ }` runs on the path strings themselves.\n\n### Aim the subject, then gate it\n\nThree dials aim a rule. Resolve them in this order.\n\n**1. Subject: the node that owns what you assert.**\n\n- A check about the **keys that a map holds** targets the map type itself.\n  The maps of responses, headers, schema properties, and paths are each their own type.\n  So \"this map must hold that key\" is one `required` on one subject. Do not search the parent.\n- A check about a **value** targets the node that holds the field, plus `property`.\n- `type: any` with a `property` checks that property everywhere it appears.\n- Confirm the type name with `inspect-node-types`, on a document of the spec version that the rule targets.\n  A named type can exist in one version and not in another.\n  `HeadersMap` is a type in OpenAPI 3. In OpenAPI 2 the response `headers` map is anonymous and has no type name, so a `HeadersMap` subject under `oas2Rules` never reports.\n  Redocly warns about an unresolved rule id, but **not** about an unresolved `subject.type`, so a wrong type gives you a rule that never reports and never complains.\n\n**2. Parent-key filters: `filterInParentKeys`, `filterOutParentKeys`, `matchParentKeys`.**\n\nThese filters match the **key of the subject node inside its parent**.\nSo they work only where that key varies: a method inside a path item, a status code inside a responses map, a path string inside a paths map.\n`matchParentKeys` takes a regular expression, so it reaches a class of keys, and a negative lookbehind inside it excludes a shape.\n\nThe common error is a filter one level too high.\nThe key of a `Responses` node is always `responses`, so `filterInParentKeys: [put]` on a `Responses` subject matches nothing and the rule never reports.\nThe method varies one level above. Reach it with `where`:\n\n```yaml\nrule/put-responses-must-include-200:\n  subject:\n    type: Responses # the key here is always `responses`, so no filter belongs on it\n  assertions:\n    required: ['200']\n  where:\n    - subject:\n        type: Operation\n        filterInParentKeys: [put] # the key that varies\n      assertions:\n        defined: true\n```\n\n**3. `where`: the ancestor gate.**\n\nBuild the gate from `--type=<Type> --parents`.\nEach chain in that output is a place where the subject sits, and the gate keeps the chain you want.\nEach entry names a type that must sit above the subject, and the rule skips every node that is not inside a matching ancestor.\nThe assertions of a gate entry are filters, never checks: when one fails, the node drops out in silence instead of becoming a problem.\nThe mechanics are exact:\n\n- Entries run in document order, outermost first. Each type can appear in one entry only.\n- The chain **can skip levels**. Name the ancestors that matter, not every node between them.\n- The subject's own type **can also appear in the last entry**.\n  That is how the gate carries a parent-key filter or an exclusion while the subject stays whole.\n- An entry of the subject's own type narrows the subject by one of its properties, such as \"this schema, when its `type` is `array`\".\n- One entry can carry both a parent-key filter and assertions, so it says \"inside an ancestor with this key **and** this value\".\n- References resolve at the gate, so entries see resolved nodes.\n- For a Schema subject with a `where`, evaluation stops at the first schema level that matches the gate.\n- A gate cannot exclude a nested `callbacks` branch.\n  An operation inside a callback still reaches a rule gated on `Paths`.\n  Say so. Do not claim that the gate excludes it.\n\nWhen one subject becomes hard to aim, write one rule for each case.\nSeveral small `rule/*` entries stay readable, and they are still better than a plugin.\n\n### Recipes\n\nA check that looks like plugin work usually needs one of these. Try them before you choose a plugin.\n\n**Gate a check about paths with the paths map.**\nThe type that holds a path also holds webhook names and callback keys, so a rule about the shape of a URL also reports those unless the gate names the paths map:\n\n```yaml\nrule/put-path-ends-with-parameter:\n  subject:\n    type: Operation\n    filterInParentKeys: [put]\n  assertions:\n    defined: false\n  where:\n    - subject:\n        type: Paths # without this entry the rule also reports webhooks and callbacks\n      assertions:\n        defined: true\n    - subject:\n        type: PathItem\n        matchParentKeys: /[^}]$/\n      assertions:\n        defined: true\n```\n\nRun `--type=<Type> --parents` on the type you want to target.\nEvery chain it prints is a place where the rule reports, and each chain you do not want needs a gate entry.\n\n**Require a value, not only a key.**\n`additionalProperties` must not sit beside `properties`, but `additionalProperties: false` stays permitted:\n\n```yaml\nrule/no-additional-properties-with-properties:\n  subject:\n    type: Schema\n    property: additionalProperties\n  assertions:\n    const: false\n  where:\n    - subject:\n        type: Schema\n        property: properties\n      assertions:\n        defined: true\n```\n\n**Guard a value gate against an absent value.**\nThe `defined: true` keeps this rule off a schema that declares no `type`:\n\n```yaml\nrule/array-items:\n  subject:\n    type: Schema\n  assertions:\n    required: [items]\n  where:\n    - subject:\n        type: Schema\n        property: type\n      assertions:\n        const: array\n        defined: true\n  message: The 'items' field is required for schemas of array type.\n```\n\nA value assertion cannot match a list, so this gate misses the OpenAPI 3.1 union form `type: [array, 'null']`.\nCover that version with a second entry, or report that the rule is narrower than the source.\n\n**Locate by ancestor, and skip the levels between.**\n\"The media types of the 200 and 400 responses of one path\" needs two gate entries, and no entry for the operation or the responses map:\n\n```yaml\nrule/health-media-type:\n  subject:\n    type: MediaTypesMap # keys, so `const` checks the media type itself\n  assertions:\n    const: application/health+json\n  where:\n    - subject:\n        type: PathItem\n        matchParentKeys: '/health'\n      assertions:\n        defined: true # a pure gate: this ancestor must exist\n    - subject:\n        type: Response\n        filterInParentKeys: ['200', '400']\n      assertions:\n        defined: true\n```\n\n**Filter the subject from the gate, and exclude with it.**\n\n```yaml\nrule/post-should-define-requestBody:\n  subject:\n    type: Operation\n  assertions:\n    required: [requestBody]\n  where:\n    - subject:\n        type: PathItem\n        matchParentKeys: /^([\\w-\\{\\}/.](?<!/actions))*$/ # the lookbehind excludes a URI shape\n      assertions:\n        defined: true\n    - subject:\n        type: Operation # the subject's own type, in the last entry\n        filterInParentKeys: [post]\n      assertions:\n        disallowed: [deprecated] # a filter, so a deprecated operation drops out\n```\n\n**Gate on the value of an ancestor**, to say \"only inside a node that looks like this\":\n\n```yaml\nwhere:\n  - subject:\n      type: Operation\n      property: deprecated\n    assertions:\n      defined: true\n      const: true\n```\n\n**Combine assertions in one rule.** All of them must hold:\n\n```yaml\nassertions:\n  requireAny: [minLength, maxLength, enum]\n  mutuallyRequired: [minLength, maxLength]\n```\n\n**Match without case.** `pattern` and `notPattern` accept inline regular expression flags, so a header-name check needs no plugin:\n\n```yaml\nassertions:\n  notPattern: '/^(authorization|content-type|accept)$/i'\n```\n\n**Assert on the reference, not on the resolved value.**\n`ref: true` requires a `$ref`, `ref: false` requires an inline value, and a string requires the unresolved `$ref` to match:\n\n```yaml\nsubject:\n  type: MediaType\n  property: schema\nassertions:\n  ref: /^#\\/components\\/.*/\n```\n\n**Bound a list.** `minLength` and `maxLength` apply to lists and to strings, so a list type as the subject limits how many members it holds. Use `maxLength: 1` for a single server.\n\n**Forbid a node type.** A subject with `defined: false` and no `property` rejects a construct anywhere in the description.\n\n**Record an exception.** When the rule is correct and a few nodes are real exceptions, keep the pattern strict and record those nodes in `.redocly.lint-ignore.yaml`.\n\n**YAML gotcha.** Quote a `message` that holds a colon followed by a space, or the config fails to parse:\n\n```yaml\nmessage: 'Schemas with type: array must define items.'\n```\n\n## Custom plugin rules\n\nDocs: [custom rules](https://redocly.com/docs/cli/custom-plugins/custom-rules).\nWorked examples: [cookbook/custom-plugin-rules](https://github.com/Redocly/redocly-cli/tree/main/cookbook/custom-plugin-rules).\nThe full visitor guide is at https://raw.githubusercontent.com/Redocly/redocly-cli/refs/heads/main/.claude/rules/walker-visitors-nodes.md.\n\nA plugin is a module. Its default export is a **function** that returns the plugin object:\n\n```js\nexport default function myPlugin() {\n  return {\n    id: 'my-plugin',\n    rules: {\n      oas3: {\n        'schema-title-defined': SchemaTitleDefined,\n      },\n    },\n  };\n}\n```\n\nRules are keyed by spec flavor: `oas2`, `oas3`, `async2`, `async3`, and `arazzo`.\nRegister the same rule under several flavors when the check applies to all of them.\n\n### Register and use\n\n```yaml\n# redocly.yaml\nplugins:\n  - ./my-plugin.js\nrules:\n  my-plugin/schema-title-defined: error\n```\n\nThe config name of the rule is `<plugin id>/<rule name>`.\n**Both halves are required.**\nA plugin whose rules are absent from `rules:` is delivered and dead.\n\n### The visitor pattern\n\nA rule is a factory that returns a visitor object keyed by node type.\nThe walker traverses the document, resolves each `$ref`, and calls your hooks.\nNever parse the document yourself, and never walk into it by hand.\n\nConfirm each visitor key with the `inspect-node-types` command.\nA key that is wrong or invented is never called, and nothing reports the mistake.\nThere is no `Reference` node, because a `$ref` and its target share the type of the target.\nWatch the singular and the plural: the map of component schemas is `NamedSchemas`, and `NamedSchema` matches nothing.\n\nKeep the rule small. Express everything through the visitors that the walker gives you.\nA plugin rule needs no traversal helper, no second walker, and no resolver of its own.\nThe walker already reaches every node of a type, and it resolves each `$ref` before `leave`.\nUse another visitor key before you write a helper.\n\n```js\nfunction SchemaTitleDefined() {\n  return {\n    Schema: {\n      enter(schema, ctx) {\n        if (schema.type === 'object' && !schema.title) {\n          ctx.report({\n            message: 'Object schemas must define a title.',\n            location: ctx.location.key(),\n          });\n        }\n      },\n    },\n  };\n}\n```\n\n- The hooks are `enter(node, ctx)`, `leave(node, ctx)`, and `skip(node, ctx)`.\n  `leave` runs after the children, when every `$ref` is resolved.\n  Write the hooks as an explicit object with `enter` and `leave` keys, as in the example.\n- Keep state that crosses nodes in the factory scope.\n  Collect in the `enter` hooks, and report in `Root.leave`.\n\n### The context object\n\nUse only these fields. The context is not the document, and it holds no path and no root.\n\n| use                                 | for                                                            |\n| ----------------------------------- | -------------------------------------------------------------- |\n| `ctx.report({ message, location })` | report the problem                                             |\n| `ctx.location.child([...])`         | point at one field                                             |\n| `ctx.location.key()`                | point at the key of the node — a report location, not a string |\n| `ctx.key`                           | the key of the node itself, as a value                         |\n| `ctx.parent`                        | the parent node                                                |\n| `ctx.resolve(ref)`                  | resolve a `$ref` to its target                                 |\n| `ctx.rawNode`                       | the unresolved node, when you need the `$ref` itself           |\n| `ctx.specVersion`                   | the spec version of the document                               |\n\nThere is no `ctx.document`, no `ctx.root`, and no `ctx.path`.\nA rule that reads one of them throws.\n\n**A rule that throws stops the whole document.**\nEvery finding from every rule is lost for that file, not only the findings of your rule.\nSo an invented context field costs much more than a rule that never reports.\n\n`ctx.key` is the reason a method or a status code belongs on the node whose key varies.\nInside a `Responses` visitor, `ctx.key` is always `responses`, so `if (ctx.key !== 'delete') return` never passes.\nThat is the same error as a parent-key filter one level too high.\n\n### Read resolved nodes, not raw children\n\nInside `enter`, a child that is a `$ref` is still a reference.\nSo `response.schema`, or the `name` of a parameter, is `undefined` for every member that is a `$ref`.\nThat turns into a false positive on an ordinary document: a uniqueness check reports \"duplicate name: undefined\", and a required-field check reports a field that is present behind the reference.\n\nUse one of these instead:\n\n- report in `leave`, where every `$ref` below the node is resolved;\n- visit the type of the child, so the walker gives you the resolved node;\n- call `ctx.resolve(ref)`.\n\nProve the rule with a fixture whose members are `$ref`s.\nA fixture with everything inline does not test this.\n"
}

SHA-256 of public snapshot: c6a6e627359e05f6134adff514a1a4c9cbcbc88c43d8f1ac036835ea62bf0828